122706Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 362525Sbostic * Copyright (c) 1988, 1993 462525Sbostic * The Regents of the University of California. All rights reserved. 533729Sbostic * 642826Sbostic * %sccs.include.redist.c% 733729Sbostic */ 822706Sdist 922706Sdist #ifndef lint 10*68717Seric static char sccsid[] = "@(#)headers.c 8.54 (Berkeley) 04/03/95"; 1133729Sbostic #endif /* not lint */ 1222706Sdist 134091Seric # include <errno.h> 144091Seric # include "sendmail.h" 154091Seric 164091Seric /* 174091Seric ** CHOMPHEADER -- process and save a header line. 184091Seric ** 194091Seric ** Called by collect and by readcf to deal with header lines. 204091Seric ** 214091Seric ** Parameters: 224091Seric ** line -- header as a text line. 234091Seric ** def -- if set, this is a default value. 24*68717Seric ** hdrp -- a pointer to the place to save the header. 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 35*68717Seric chompheader(line, def, hdrp, e) 364091Seric char *line; 374091Seric bool def; 38*68717Seric HDR **hdrp; 3955012Seric register ENVELOPE *e; 404091Seric { 414091Seric register char *p; 424091Seric register HDR *h; 434091Seric HDR **hp; 444091Seric char *fname; 454091Seric char *fvalue; 464091Seric struct hdrinfo *hi; 479059Seric bool cond = FALSE; 48*68717Seric bool headeronly; 4910689Seric BITMAP mopts; 5068528Seric char buf[MAXNAME + 1]; 514091Seric 527677Seric if (tTd(31, 6)) 537677Seric printf("chompheader: %s\n", line); 547677Seric 55*68717Seric headeronly = hdrp != NULL; 56*68717Seric if (!headeronly) 57*68717Seric hdrp = &e->e_header; 58*68717Seric 594627Seric /* strip off options */ 6010689Seric clrbitmap(mopts); 614627Seric p = line; 6257405Seric if (*p == '?') 634627Seric { 644627Seric /* have some */ 6556795Seric register char *q = strchr(p + 1, *p); 664627Seric 674627Seric if (q != NULL) 684627Seric { 694627Seric *q++ = '\0'; 7010689Seric while (*++p != '\0') 7110689Seric setbitn(*p, mopts); 724627Seric p = q; 734627Seric } 744627Seric else 7568690Seric syserr("553 header syntax error, line \"%s\"", line); 769059Seric cond = TRUE; 774627Seric } 784627Seric 794091Seric /* find canonical name */ 804627Seric fname = p; 8164149Seric while (isascii(*p) && isgraph(*p) && *p != ':') 8264149Seric p++; 8364149Seric fvalue = p; 8464149Seric while (isascii(*p) && isspace(*p)) 8564149Seric p++; 8664150Seric if (*p++ != ':' || fname == fvalue) 8710118Seric { 8858151Seric syserr("553 header syntax error, line \"%s\"", line); 8910118Seric return (0); 9010118Seric } 9164149Seric *fvalue = '\0'; 9264149Seric fvalue = p; 934091Seric 944091Seric /* strip field value on front */ 954091Seric if (*fvalue == ' ') 964091Seric fvalue++; 974091Seric 984091Seric /* see if it is a known type */ 994091Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1004091Seric { 10159579Seric if (strcasecmp(hi->hi_field, fname) == 0) 1024091Seric break; 1034091Seric } 1044091Seric 10564283Seric if (tTd(31, 9)) 10664283Seric { 10764283Seric if (hi->hi_field == NULL) 10864283Seric printf("no header match\n"); 10964283Seric else 11067694Seric printf("header match, hi_flags=%x\n", hi->hi_flags); 11164283Seric } 11264283Seric 11311414Seric /* see if this is a resent message */ 114*68717Seric if (!def && !headeronly && bitset(H_RESENT, hi->hi_flags)) 11555012Seric e->e_flags |= EF_RESENT; 11611414Seric 11768558Seric /* if this is an Errors-To: header keep track of it now */ 118*68717Seric if (UseErrorsTo && !def && !headeronly && 119*68717Seric bitset(H_ERRORSTO, hi->hi_flags)) 12068558Seric (void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e); 12168558Seric 1224091Seric /* if this means "end of header" quit now */ 1234091Seric if (bitset(H_EOH, hi->hi_flags)) 1244091Seric return (hi->hi_flags); 1254091Seric 12664653Seric /* 12764653Seric ** Drop explicit From: if same as what we would generate. 12864653Seric ** This is to make MH (which doesn't always give a full name) 12964653Seric ** insert the full name information in all circumstances. 13064653Seric */ 13164653Seric 13214784Seric p = "resent-from"; 13355012Seric if (!bitset(EF_RESENT, e->e_flags)) 13414784Seric p += 7; 135*68717Seric if (!def && !headeronly && !bitset(EF_QUEUERUN, e->e_flags) && 136*68717Seric strcasecmp(fname, p) == 0) 13711414Seric { 13863753Seric if (tTd(31, 2)) 13963753Seric { 14063753Seric printf("comparing header from (%s) against default (%s or %s)\n", 14163753Seric fvalue, e->e_from.q_paddr, e->e_from.q_user); 14263753Seric } 14355012Seric if (e->e_from.q_paddr != NULL && 14463753Seric (strcmp(fvalue, e->e_from.q_paddr) == 0 || 14563753Seric strcmp(fvalue, e->e_from.q_user) == 0)) 14611414Seric return (hi->hi_flags); 14764351Seric #ifdef MAYBENEXTRELEASE /* XXX UNTESTED XXX UNTESTED XXX UNTESTED XXX */ 14864351Seric #ifdef USERDB 14964351Seric else 15064351Seric { 15164351Seric auto ADDRESS a; 15264351Seric char *fancy; 15366123Seric bool oldSuprErrs = SuprErrs; 15464351Seric extern char *crackaddr(); 15564351Seric extern char *udbsender(); 15664351Seric 15764653Seric /* 15864653Seric ** Try doing USERDB rewriting even on fully commented 15964653Seric ** names; this saves the "comment" information (such 16064653Seric ** as full name) and rewrites the electronic part. 16166106Seric ** 16266106Seric ** XXX This code doesn't belong here -- parsing should 16366106Seric ** XXX not be done during collect() phase because 16466106Seric ** XXX error messages can confuse the SMTP phase. 16566123Seric ** XXX Setting SuprErrs is a crude hack around this 16666106Seric ** XXX problem. 16764653Seric */ 16864653Seric 16966106Seric if (OpMode == MD_SMTP || OpMode == MD_ARPAFTP) 17066123Seric SuprErrs = TRUE; 17164351Seric fancy = crackaddr(fvalue); 17264351Seric if (parseaddr(fvalue, &a, RF_COPYNONE, '\0', NULL, e) != NULL && 17367472Seric bitnset(M_CHECKUDB, a.q_mailer->m_flags) && 17464351Seric (p = udbsender(a.q_user)) != NULL) 17564351Seric { 17664351Seric char *oldg = macvalue('g', e); 17764351Seric 17864351Seric define('g', p, e); 17968529Seric expand(fancy, buf, sizeof buf, e); 18064351Seric define('g', oldg, e); 18164351Seric fvalue = buf; 18264351Seric } 18366123Seric SuprErrs = oldSuprErrs; 18464351Seric } 18564351Seric #endif 18664351Seric #endif 18711414Seric } 18811414Seric 18914784Seric /* delete default value for this header */ 190*68717Seric for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link) 19114784Seric { 19259579Seric if (strcasecmp(fname, h->h_field) == 0 && 19314784Seric bitset(H_DEFAULT, h->h_flags) && 19414784Seric !bitset(H_FORCE, h->h_flags)) 19514784Seric h->h_value = NULL; 19614784Seric } 19714784Seric 19813012Seric /* create a new node */ 19913012Seric h = (HDR *) xalloc(sizeof *h); 20013012Seric h->h_field = newstr(fname); 20164134Seric h->h_value = newstr(fvalue); 20213012Seric h->h_link = NULL; 20323117Seric bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts); 20413012Seric *hp = h; 2058066Seric h->h_flags = hi->hi_flags; 2064091Seric if (def) 2074091Seric h->h_flags |= H_DEFAULT; 2089059Seric if (cond) 2099059Seric h->h_flags |= H_CHECK; 2104091Seric 2115937Seric /* hack to see if this is a new format message */ 212*68717Seric if (!def && !headeronly && bitset(H_RCPT|H_FROM, h->h_flags) && 21356795Seric (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL || 21456795Seric strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL)) 2158089Seric { 21655012Seric e->e_flags &= ~EF_OLDSTYLE; 2178089Seric } 2185937Seric 2194091Seric return (h->h_flags); 2204091Seric } 2214091Seric /* 2226980Seric ** ADDHEADER -- add a header entry to the end of the queue. 2236980Seric ** 2246980Seric ** This bypasses the special checking of chompheader. 2256980Seric ** 2266980Seric ** Parameters: 22759579Seric ** field -- the name of the header field. 22858789Seric ** value -- the value of the field. 22967546Seric ** hp -- an indirect pointer to the header structure list. 2306980Seric ** 2316980Seric ** Returns: 2326980Seric ** none. 2336980Seric ** 2346980Seric ** Side Effects: 2356980Seric ** adds the field on the list of headers for this envelope. 2366980Seric */ 2376980Seric 23867546Seric addheader(field, value, hdrlist) 2396980Seric char *field; 2406980Seric char *value; 24167546Seric HDR **hdrlist; 2426980Seric { 2436980Seric register HDR *h; 2446980Seric register struct hdrinfo *hi; 2456980Seric HDR **hp; 2466980Seric 2476980Seric /* find info struct */ 2486980Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 2496980Seric { 25059579Seric if (strcasecmp(field, hi->hi_field) == 0) 2516980Seric break; 2526980Seric } 2536980Seric 2546980Seric /* find current place in list -- keep back pointer? */ 25567546Seric for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link) 2566980Seric { 25759579Seric if (strcasecmp(field, h->h_field) == 0) 2586980Seric break; 2596980Seric } 2606980Seric 2616980Seric /* allocate space for new header */ 2626980Seric h = (HDR *) xalloc(sizeof *h); 2636980Seric h->h_field = field; 2646980Seric h->h_value = newstr(value); 2657368Seric h->h_link = *hp; 2666980Seric h->h_flags = hi->hi_flags | H_DEFAULT; 26710689Seric clrbitmap(h->h_mflags); 2686980Seric *hp = h; 2696980Seric } 2706980Seric /* 2714091Seric ** HVALUE -- return value of a header. 2724091Seric ** 2734091Seric ** Only "real" fields (i.e., ones that have not been supplied 2744091Seric ** as a default) are used. 2754091Seric ** 2764091Seric ** Parameters: 2774091Seric ** field -- the field name. 27867546Seric ** header -- the header list. 2794091Seric ** 2804091Seric ** Returns: 2814091Seric ** pointer to the value part. 2824091Seric ** NULL if not found. 2834091Seric ** 2844091Seric ** Side Effects: 2859382Seric ** none. 2864091Seric */ 2874091Seric 2884091Seric char * 28967546Seric hvalue(field, header) 2904091Seric char *field; 29167546Seric HDR *header; 2924091Seric { 2934091Seric register HDR *h; 2944091Seric 29567546Seric for (h = header; h != NULL; h = h->h_link) 2964091Seric { 29759579Seric if (!bitset(H_DEFAULT, h->h_flags) && 29859579Seric strcasecmp(h->h_field, field) == 0) 2994091Seric return (h->h_value); 3004091Seric } 3014091Seric return (NULL); 3024091Seric } 3034091Seric /* 3044091Seric ** ISHEADER -- predicate telling if argument is a header. 3054091Seric ** 3064319Seric ** A line is a header if it has a single word followed by 3074319Seric ** optional white space followed by a colon. 3084319Seric ** 309*68717Seric ** Header fields beginning with two dashes, although technically 310*68717Seric ** permitted by RFC822, are automatically rejected in order 311*68717Seric ** to make MIME work out. Without this we could have a technically 312*68717Seric ** legal header such as ``--"foo:bar"'' that would also be a legal 313*68717Seric ** MIME separator. 314*68717Seric ** 3154091Seric ** Parameters: 31668690Seric ** h -- string to check for possible headerness. 3174091Seric ** 3184091Seric ** Returns: 31968690Seric ** TRUE if h is a header. 3204091Seric ** FALSE otherwise. 3214091Seric ** 3224091Seric ** Side Effects: 3234091Seric ** none. 3244091Seric */ 3254091Seric 3264091Seric bool 32768690Seric isheader(h) 32868690Seric char *h; 3294091Seric { 33068690Seric register char *s = h; 33168690Seric 332*68717Seric if (s[0] == '-' && s[1] == '-') 333*68717Seric return FALSE; 334*68717Seric 3359382Seric while (*s > ' ' && *s != ':' && *s != '\0') 3364091Seric s++; 3379382Seric 33868690Seric if (h == s) 33968690Seric return FALSE; 34068690Seric 3419382Seric /* following technically violates RFC822 */ 34258050Seric while (isascii(*s) && isspace(*s)) 3434091Seric s++; 3449382Seric 3454091Seric return (*s == ':'); 3464091Seric } 3475919Seric /* 3487783Seric ** EATHEADER -- run through the stored header and extract info. 3497783Seric ** 3507783Seric ** Parameters: 3519382Seric ** e -- the envelope to process. 35258929Seric ** full -- if set, do full processing (e.g., compute 35358929Seric ** message priority). 3547783Seric ** 3557783Seric ** Returns: 3567783Seric ** none. 3577783Seric ** 3587783Seric ** Side Effects: 3597783Seric ** Sets a bunch of global variables from information 3609382Seric ** in the collected header. 3619382Seric ** Aborts the message if the hop count is exceeded. 3627783Seric */ 3637783Seric 36458929Seric eatheader(e, full) 3659382Seric register ENVELOPE *e; 36658929Seric bool full; 3677783Seric { 3687783Seric register HDR *h; 3697783Seric register char *p; 3709382Seric int hopcnt = 0; 37157208Seric char *msgid; 37258688Seric char buf[MAXLINE]; 3737783Seric 37458688Seric /* 37558688Seric ** Set up macros for possible expansion in headers. 37658688Seric */ 37758688Seric 37858704Seric define('f', e->e_sender, e); 37958704Seric define('g', e->e_sender, e); 38064148Seric if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0') 38164148Seric define('u', e->e_origrcpt, e); 38264148Seric else 38364148Seric define('u', NULL, e); 38458688Seric 38565052Seric /* full name of from person */ 38667546Seric p = hvalue("full-name", e->e_header); 38765052Seric if (p != NULL) 38865052Seric define('x', p, e); 38965052Seric 3909382Seric if (tTd(32, 1)) 3919382Seric printf("----- collected header -----\n"); 39257208Seric msgid = "<none>"; 3939382Seric for (h = e->e_header; h != NULL; h = h->h_link) 3947783Seric { 39564156Seric if (h->h_value == NULL) 39664156Seric { 39764156Seric if (tTd(32, 1)) 39864156Seric printf("%s: <NULL>\n", h->h_field); 39964156Seric continue; 40064156Seric } 40164156Seric 40258688Seric /* do early binding */ 40364156Seric if (bitset(H_DEFAULT, h->h_flags)) 40458688Seric { 40568529Seric expand(h->h_value, buf, sizeof buf, e); 40658688Seric if (buf[0] != '\0') 40758688Seric { 40858688Seric h->h_value = newstr(buf); 40958688Seric h->h_flags &= ~H_DEFAULT; 41058688Seric } 41158688Seric } 41258688Seric 4139382Seric if (tTd(32, 1)) 41465152Seric { 41565152Seric printf("%s: ", h->h_field); 41665152Seric xputs(h->h_value); 41765152Seric printf("\n"); 41865152Seric } 41957359Seric 42011414Seric /* count the number of times it has been processed */ 4219382Seric if (bitset(H_TRACE, h->h_flags)) 4229382Seric hopcnt++; 42311414Seric 42411414Seric /* send to this person if we so desire */ 42511414Seric if (GrabTo && bitset(H_RCPT, h->h_flags) && 42611414Seric !bitset(H_DEFAULT, h->h_flags) && 42755012Seric (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags))) 42811414Seric { 42963839Seric int saveflags = e->e_flags; 43063839Seric 43164283Seric (void) sendtolist(h->h_value, NULLADDR, 43267982Seric &e->e_sendqueue, 0, e); 43363839Seric 43463839Seric /* delete fatal errors generated by this address */ 43564148Seric if (!GrabTo && !bitset(EF_FATALERRS, saveflags)) 43663839Seric e->e_flags &= ~EF_FATALERRS; 43711414Seric } 43811414Seric 43957208Seric /* save the message-id for logging */ 44064156Seric if (full && strcasecmp(h->h_field, "message-id") == 0) 44111290Seric { 44257208Seric msgid = h->h_value; 44359859Seric while (isascii(*msgid) && isspace(*msgid)) 44459859Seric msgid++; 44511290Seric } 44657359Seric 44757359Seric /* see if this is a return-receipt header */ 44857359Seric if (bitset(H_RECEIPTTO, h->h_flags)) 44957359Seric e->e_receiptto = h->h_value; 4509382Seric } 4519382Seric if (tTd(32, 1)) 4527783Seric printf("----------------------------\n"); 4537783Seric 45458145Seric /* if we are just verifying (that is, sendmail -t -bv), drop out now */ 45558145Seric if (OpMode == MD_VERIFY) 45658145Seric return; 45758145Seric 4589382Seric /* store hop count */ 4599382Seric if (hopcnt > e->e_hopcount) 4609382Seric e->e_hopcount = hopcnt; 4619382Seric 4627783Seric /* message priority */ 46367546Seric p = hvalue("precedence", e->e_header); 4649382Seric if (p != NULL) 4659382Seric e->e_class = priencode(p); 46658929Seric if (full) 46767730Seric { 46825013Seric e->e_msgpriority = e->e_msgsize 46924981Seric - e->e_class * WkClassFact 47024981Seric + e->e_nrcpts * WkRecipFact; 47167730Seric if (e->e_class < 0) 47267730Seric e->e_timeoutclass = TOC_NONURGENT; 47367730Seric else if (e->e_class > 0) 47467730Seric e->e_timeoutclass = TOC_URGENT; 47567730Seric } 4767783Seric 47767730Seric /* message timeout priority */ 47867730Seric p = hvalue("priority", e->e_header); 47967730Seric if (full && p != NULL) 48067730Seric { 48167730Seric /* (this should be in the configuration file) */ 48267730Seric if (strcasecmp(p, "urgent")) 48367730Seric e->e_timeoutclass = TOC_URGENT; 48467730Seric else if (strcasecmp(p, "normal")) 48567730Seric e->e_timeoutclass = TOC_NORMAL; 48667730Seric else if (strcasecmp(p, "non-urgent")) 48767730Seric e->e_timeoutclass = TOC_NONURGENT; 48867730Seric } 48967730Seric 4907783Seric /* date message originated */ 49167546Seric p = hvalue("posted-date", e->e_header); 4927783Seric if (p == NULL) 49367546Seric p = hvalue("date", e->e_header); 4947783Seric if (p != NULL) 4959382Seric define('a', p, e); 49611290Seric 49711290Seric /* 49865983Seric ** From person in antiquated ARPANET mode 49965983Seric ** required by UK Grey Book e-mail gateways (sigh) 50065983Seric */ 50165983Seric 50265983Seric if (OpMode == MD_ARPAFTP) 50365983Seric { 50465983Seric register struct hdrinfo *hi; 50565983Seric 50665983Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 50765983Seric { 50865983Seric if (bitset(H_FROM, hi->hi_flags) && 50965983Seric (!bitset(H_RESENT, hi->hi_flags) || 51065983Seric bitset(EF_RESENT, e->e_flags)) && 51167546Seric (p = hvalue(hi->hi_field, e->e_header)) != NULL) 51265983Seric break; 51365983Seric } 51465983Seric if (hi->hi_field != NULL) 51565983Seric { 51665983Seric if (tTd(32, 2)) 51765983Seric printf("eatheader: setsender(*%s == %s)\n", 51865983Seric hi->hi_field, p); 51965983Seric setsender(p, e, NULL, TRUE); 52065983Seric } 52165983Seric } 52265983Seric 52365983Seric /* 52411290Seric ** Log collection information. 52511290Seric */ 52611290Seric 52711290Seric # ifdef LOG 52868036Seric if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4) 52965089Seric logsender(e, msgid); 53065089Seric # endif /* LOG */ 53165089Seric e->e_flags &= ~EF_LOGSENDER; 53265089Seric } 53365089Seric /* 53465089Seric ** LOGSENDER -- log sender information 53565089Seric ** 53665089Seric ** Parameters: 53765089Seric ** e -- the envelope to log 53865089Seric ** msgid -- the message id 53965089Seric ** 54065089Seric ** Returns: 54165089Seric ** none 54265089Seric */ 54365089Seric 54465089Seric logsender(e, msgid) 54565089Seric register ENVELOPE *e; 54665089Seric char *msgid; 54765089Seric { 54866748Seric # ifdef LOG 54965089Seric char *name; 55065089Seric register char *sbp; 55165089Seric register char *p; 55267901Seric int l; 55368528Seric char hbuf[MAXNAME + 1]; 55468528Seric char sbuf[MAXLINE + 1]; 55568528Seric char mbuf[MAXNAME + 1]; 55665089Seric 55767901Seric /* don't allow newlines in the message-id */ 55867901Seric if (msgid != NULL) 55967901Seric { 56067901Seric l = strlen(msgid); 56167901Seric if (l > sizeof mbuf - 1) 56267901Seric l = sizeof mbuf - 1; 56367901Seric bcopy(msgid, mbuf, l); 56467901Seric mbuf[l] = '\0'; 56567901Seric p = mbuf; 56667901Seric while ((p = strchr(p, '\n')) != NULL) 56767901Seric *p++ = ' '; 56867901Seric } 56967901Seric 57065089Seric if (bitset(EF_RESPONSE, e->e_flags)) 57165089Seric name = "[RESPONSE]"; 57265089Seric else if ((name = macvalue('_', e)) != NULL) 57365089Seric ; 57466003Seric else if (RealHostName == NULL) 57566003Seric name = "localhost"; 57665089Seric else if (RealHostName[0] == '[') 57765089Seric name = RealHostName; 57865089Seric else 57911290Seric { 58065089Seric name = hbuf; 58165089Seric (void) sprintf(hbuf, "%.80s", RealHostName); 58265089Seric if (RealHostAddr.sa.sa_family != 0) 58357359Seric { 58465089Seric p = &hbuf[strlen(hbuf)]; 58565089Seric (void) sprintf(p, " (%s)", 58665089Seric anynet_ntoa(&RealHostAddr)); 58757359Seric } 58865089Seric } 58957359Seric 59065089Seric /* some versions of syslog only take 5 printf args */ 59165059Seric # if (SYSLOG_BUFSIZE) >= 256 59265089Seric sbp = sbuf; 59365089Seric sprintf(sbp, "from=%.200s, size=%ld, class=%d, pri=%ld, nrcpts=%d", 59467788Seric e->e_from.q_paddr == NULL ? "<NONE>" : e->e_from.q_paddr, 59567788Seric e->e_msgsize, e->e_class, e->e_msgpriority, e->e_nrcpts); 59665089Seric sbp += strlen(sbp); 59765089Seric if (msgid != NULL) 59865089Seric { 59967901Seric sprintf(sbp, ", msgid=%.100s", mbuf); 60060575Seric sbp += strlen(sbp); 60165089Seric } 60265089Seric if (e->e_bodytype != NULL) 60365089Seric { 60465089Seric (void) sprintf(sbp, ", bodytype=%.20s", e->e_bodytype); 60565089Seric sbp += strlen(sbp); 60665089Seric } 60765089Seric p = macvalue('r', e); 60865089Seric if (p != NULL) 60965089Seric (void) sprintf(sbp, ", proto=%.20s", p); 61065089Seric syslog(LOG_INFO, "%s: %s, relay=%s", 61165089Seric e->e_id, sbuf, name); 61265059Seric 61365059Seric # else /* short syslog buffer */ 61465059Seric 61565089Seric syslog(LOG_INFO, "%s: from=%s", 61667788Seric e->e_id, e->e_from.q_paddr == NULL ? "<NONE>" : 61767788Seric shortenstring(e->e_from.q_paddr, 83)); 61865089Seric syslog(LOG_INFO, "%s: size=%ld, class=%ld, pri=%ld, nrcpts=%d", 61965089Seric e->e_id, e->e_msgsize, e->e_class, 62065089Seric e->e_msgpriority, e->e_nrcpts); 62165089Seric if (msgid != NULL) 62267901Seric syslog(LOG_INFO, "%s: msgid=%s", e->e_id, mbuf); 62365650Seric sbp = sbuf; 62465650Seric sprintf(sbp, "%s:", e->e_id); 62565650Seric sbp += strlen(sbp); 62665089Seric if (e->e_bodytype != NULL) 62765650Seric { 62865650Seric sprintf(sbp, " bodytype=%s,", e->e_bodytype); 62965650Seric sbp += strlen(sbp); 63065650Seric } 63165089Seric p = macvalue('r', e); 63265089Seric if (p != NULL) 63365650Seric { 63465731Seric sprintf(sbp, " proto=%s,", p); 63565650Seric sbp += strlen(sbp); 63665650Seric } 63765650Seric syslog(LOG_INFO, "%s relay=%s", sbuf, name); 63865059Seric # endif 63966748Seric # endif 6407783Seric } 6417783Seric /* 6427783Seric ** PRIENCODE -- encode external priority names into internal values. 6437783Seric ** 6447783Seric ** Parameters: 6457783Seric ** p -- priority in ascii. 6467783Seric ** 6477783Seric ** Returns: 6487783Seric ** priority as a numeric level. 6497783Seric ** 6507783Seric ** Side Effects: 6517783Seric ** none. 6527783Seric */ 6537783Seric 6547783Seric priencode(p) 6557783Seric char *p; 6567783Seric { 6578253Seric register int i; 6587783Seric 6598253Seric for (i = 0; i < NumPriorities; i++) 6607783Seric { 66133725Sbostic if (!strcasecmp(p, Priorities[i].pri_name)) 6628253Seric return (Priorities[i].pri_val); 6637783Seric } 6648253Seric 6658253Seric /* unknown priority */ 6668253Seric return (0); 6677783Seric } 6687783Seric /* 6697890Seric ** CRACKADDR -- parse an address and turn it into a macro 6707783Seric ** 6717783Seric ** This doesn't actually parse the address -- it just extracts 6727783Seric ** it and replaces it with "$g". The parse is totally ad hoc 6737783Seric ** and isn't even guaranteed to leave something syntactically 6747783Seric ** identical to what it started with. However, it does leave 6757783Seric ** something semantically identical. 6767783Seric ** 67751379Seric ** This algorithm has been cleaned up to handle a wider range 67851379Seric ** of cases -- notably quoted and backslash escaped strings. 67951379Seric ** This modification makes it substantially better at preserving 68051379Seric ** the original syntax. 6817783Seric ** 6827783Seric ** Parameters: 6837890Seric ** addr -- the address to be cracked. 6847783Seric ** 6857783Seric ** Returns: 6867783Seric ** a pointer to the new version. 6877783Seric ** 6887783Seric ** Side Effects: 6897890Seric ** none. 6907783Seric ** 6917783Seric ** Warning: 6927783Seric ** The return value is saved in local storage and should 6937783Seric ** be copied if it is to be reused. 6947783Seric */ 6957783Seric 6967783Seric char * 6977890Seric crackaddr(addr) 6987890Seric register char *addr; 6997783Seric { 7007783Seric register char *p; 70151379Seric register char c; 70251379Seric int cmtlev; 70356764Seric int realcmtlev; 70456764Seric int anglelev, realanglelev; 70551379Seric int copylev; 70651379Seric bool qmode; 70756764Seric bool realqmode; 70856764Seric bool skipping; 70951379Seric bool putgmac = FALSE; 71056735Seric bool quoteit = FALSE; 71164148Seric bool gotangle = FALSE; 71251379Seric register char *bp; 71356764Seric char *buflim; 71468528Seric static char buf[MAXNAME + 1]; 7157783Seric 7167783Seric if (tTd(33, 1)) 7177890Seric printf("crackaddr(%s)\n", addr); 7187783Seric 7198082Seric /* strip leading spaces */ 72058050Seric while (*addr != '\0' && isascii(*addr) && isspace(*addr)) 7218082Seric addr++; 7228082Seric 7237783Seric /* 72451379Seric ** Start by assuming we have no angle brackets. This will be 72551379Seric ** adjusted later if we find them. 7267783Seric */ 7277783Seric 72851379Seric bp = buf; 72956764Seric buflim = &buf[sizeof buf - 5]; 73051379Seric p = addr; 73156764Seric copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0; 73256764Seric qmode = realqmode = FALSE; 73351379Seric 73451379Seric while ((c = *p++) != '\0') 7357783Seric { 73656764Seric /* 73756764Seric ** If the buffer is overful, go into a special "skipping" 73856764Seric ** mode that tries to keep legal syntax but doesn't actually 73956764Seric ** output things. 74056764Seric */ 7417783Seric 74256764Seric skipping = bp >= buflim; 74356735Seric 74456764Seric if (copylev > 0 && !skipping) 74556764Seric *bp++ = c; 74656735Seric 74751379Seric /* check for backslash escapes */ 74851379Seric if (c == '\\') 7497783Seric { 75058890Seric /* arrange to quote the address */ 75158890Seric if (cmtlev <= 0 && !qmode) 75258890Seric quoteit = TRUE; 75358890Seric 75451379Seric if ((c = *p++) == '\0') 7557783Seric { 75651379Seric /* too far */ 75751379Seric p--; 75851379Seric goto putg; 7597783Seric } 76056764Seric if (copylev > 0 && !skipping) 76151379Seric *bp++ = c; 76251379Seric goto putg; 7637783Seric } 7647783Seric 76551379Seric /* check for quoted strings */ 76664967Seric if (c == '"' && cmtlev <= 0) 7677783Seric { 76851379Seric qmode = !qmode; 76956764Seric if (copylev > 0 && !skipping) 77056764Seric realqmode = !realqmode; 77151379Seric continue; 7727783Seric } 77351379Seric if (qmode) 77451379Seric goto putg; 7757783Seric 77651379Seric /* check for comments */ 77751379Seric if (c == '(') 7787783Seric { 77951379Seric cmtlev++; 78056764Seric 78156764Seric /* allow space for closing paren */ 78256764Seric if (!skipping) 78356764Seric { 78456764Seric buflim--; 78556764Seric realcmtlev++; 78656764Seric if (copylev++ <= 0) 78756764Seric { 78856764Seric *bp++ = ' '; 78956764Seric *bp++ = c; 79056764Seric } 79156764Seric } 79251379Seric } 79351379Seric if (cmtlev > 0) 79451379Seric { 79551379Seric if (c == ')') 7967783Seric { 79751379Seric cmtlev--; 79851379Seric copylev--; 79956764Seric if (!skipping) 80056764Seric { 80156764Seric realcmtlev--; 80256764Seric buflim++; 80356764Seric } 8047783Seric } 8057783Seric continue; 8067783Seric } 80756764Seric else if (c == ')') 80856764Seric { 80956764Seric /* syntax error: unmatched ) */ 81065544Seric if (copylev > 0 && !skipping) 81156764Seric bp--; 81256764Seric } 8137783Seric 81456764Seric /* check for characters that may have to be quoted */ 81564148Seric if (strchr(".'@,;:\\()[]", c) != NULL) 81656764Seric { 81756764Seric /* 81856764Seric ** If these occur as the phrase part of a <> 81956764Seric ** construct, but are not inside of () or already 82056764Seric ** quoted, they will have to be quoted. Note that 82156764Seric ** now (but don't actually do the quoting). 82256764Seric */ 82356764Seric 82456764Seric if (cmtlev <= 0 && !qmode) 82556764Seric quoteit = TRUE; 82656764Seric } 82756764Seric 82851379Seric /* check for angle brackets */ 82951379Seric if (c == '<') 83051379Seric { 83156735Seric register char *q; 83256735Seric 83364148Seric /* assume first of two angles is bogus */ 83464148Seric if (gotangle) 83564148Seric quoteit = TRUE; 83664148Seric gotangle = TRUE; 83764148Seric 83851379Seric /* oops -- have to change our mind */ 83964752Seric anglelev = 1; 84056764Seric if (!skipping) 84164752Seric realanglelev = 1; 84256764Seric 84356735Seric bp = buf; 84456735Seric if (quoteit) 84556735Seric { 84656735Seric *bp++ = '"'; 84756735Seric 84856735Seric /* back up over the '<' and any spaces */ 84956735Seric --p; 85058050Seric while (isascii(*--p) && isspace(*p)) 85156735Seric continue; 85256735Seric p++; 85356735Seric } 85456735Seric for (q = addr; q < p; ) 85556735Seric { 85656735Seric c = *q++; 85756764Seric if (bp < buflim) 85856764Seric { 85956764Seric if (quoteit && c == '"') 86056764Seric *bp++ = '\\'; 86156764Seric *bp++ = c; 86256764Seric } 86356735Seric } 86456735Seric if (quoteit) 86556735Seric { 86664148Seric if (bp == &buf[1]) 86764148Seric bp--; 86864148Seric else 86964148Seric *bp++ = '"'; 87056764Seric while ((c = *p++) != '<') 87156764Seric { 87256764Seric if (bp < buflim) 87356764Seric *bp++ = c; 87456764Seric } 87556764Seric *bp++ = c; 87656735Seric } 87751379Seric copylev = 0; 87856735Seric putgmac = quoteit = FALSE; 87951379Seric continue; 88051379Seric } 8817783Seric 88251379Seric if (c == '>') 8837783Seric { 88456764Seric if (anglelev > 0) 88556764Seric { 88656764Seric anglelev--; 88756764Seric if (!skipping) 88856764Seric { 88956764Seric realanglelev--; 89056764Seric buflim++; 89156764Seric } 89256764Seric } 89356764Seric else if (!skipping) 89456764Seric { 89556764Seric /* syntax error: unmatched > */ 89656764Seric if (copylev > 0) 89756764Seric bp--; 89864752Seric quoteit = TRUE; 89956764Seric continue; 90056764Seric } 90151379Seric if (copylev++ <= 0) 90251379Seric *bp++ = c; 90351379Seric continue; 9047783Seric } 90551379Seric 90651379Seric /* must be a real address character */ 90751379Seric putg: 90851379Seric if (copylev <= 0 && !putgmac) 90951379Seric { 91058050Seric *bp++ = MACROEXPAND; 91151379Seric *bp++ = 'g'; 91251379Seric putgmac = TRUE; 91351379Seric } 9147783Seric } 9157783Seric 91656764Seric /* repair any syntactic damage */ 91756764Seric if (realqmode) 91856764Seric *bp++ = '"'; 91956764Seric while (realcmtlev-- > 0) 92056764Seric *bp++ = ')'; 92156764Seric while (realanglelev-- > 0) 92256764Seric *bp++ = '>'; 92351379Seric *bp++ = '\0'; 9247783Seric 9257783Seric if (tTd(33, 1)) 9267944Seric printf("crackaddr=>`%s'\n", buf); 9277783Seric 9287783Seric return (buf); 9297783Seric } 9309382Seric /* 9319382Seric ** PUTHEADER -- put the header part of a message from the in-core copy 9329382Seric ** 9339382Seric ** Parameters: 93465870Seric ** mci -- the connection information. 93567546Seric ** h -- the header to put. 9369382Seric ** e -- envelope to use. 9379382Seric ** 9389382Seric ** Returns: 9399382Seric ** none. 9409382Seric ** 9419382Seric ** Side Effects: 9429382Seric ** none. 9439382Seric */ 9449382Seric 94557589Seric /* 94657589Seric * Macro for fast max (not available in e.g. DG/UX, 386/ix). 94757589Seric */ 94857589Seric #ifndef MAX 94957589Seric # define MAX(a,b) (((a)>(b))?(a):(b)) 95057589Seric #endif 95157589Seric 95268228Seric putheader(mci, h, e) 95365870Seric register MCI *mci; 95467546Seric register HDR *h; 9559382Seric register ENVELOPE *e; 9569382Seric { 95757135Seric char buf[MAX(MAXLINE,BUFSIZ)]; 95857135Seric char obuf[MAXLINE]; 9599382Seric 96059882Seric if (tTd(34, 1)) 96165870Seric printf("--- putheader, mailer = %s ---\n", 96265870Seric mci->mci_mailer->m_name); 96359882Seric 96467546Seric mci->mci_flags |= MCIF_INHEADER; 96567546Seric for (; h != NULL; h = h->h_link) 9669382Seric { 9679382Seric register char *p; 96810689Seric extern bool bitintersect(); 9699382Seric 97059882Seric if (tTd(34, 11)) 97159882Seric { 97259882Seric printf(" %s: ", h->h_field); 97359882Seric xputs(h->h_value); 97459882Seric } 97559882Seric 9769382Seric if (bitset(H_CHECK|H_ACHECK, h->h_flags) && 97765870Seric !bitintersect(h->h_mflags, mci->mci_mailer->m_flags)) 97859882Seric { 97959882Seric if (tTd(34, 11)) 98059882Seric printf(" (skipped)\n"); 9819382Seric continue; 98259882Seric } 9839382Seric 98411414Seric /* handle Resent-... headers specially */ 98511414Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 98659882Seric { 98759882Seric if (tTd(34, 11)) 98859882Seric printf(" (skipped (resent))\n"); 98911414Seric continue; 99059882Seric } 99111414Seric 99266784Seric /* suppress return receipts if requested */ 99366784Seric if (bitset(H_RECEIPTTO, h->h_flags) && 99466784Seric bitset(EF_NORECEIPT, e->e_flags)) 99566784Seric { 99666784Seric if (tTd(34, 11)) 99766784Seric printf(" (skipped (receipt))\n"); 99866784Seric continue; 99966784Seric } 100066784Seric 100167694Seric /* suppress Content-Transfer-Encoding: if we are MIMEing */ 100267694Seric if (bitset(H_CTE, h->h_flags) && 100368228Seric bitset(MCIF_CVT8TO7, mci->mci_flags)) 100467694Seric { 100567694Seric if (tTd(34, 11)) 100667694Seric printf(" (skipped (content-transfer-encoding))\n"); 100767694Seric continue; 100867694Seric } 100967694Seric 101065152Seric /* macro expand value if generated internally */ 10119382Seric p = h->h_value; 10129382Seric if (bitset(H_DEFAULT, h->h_flags)) 10139382Seric { 101468529Seric expand(p, buf, sizeof buf, e); 10159382Seric p = buf; 10169382Seric if (p == NULL || *p == '\0') 101765152Seric { 101865152Seric if (tTd(34, 11)) 101965152Seric printf(" (skipped -- null value)\n"); 10209382Seric continue; 102165152Seric } 10229382Seric } 10239382Seric 102465152Seric if (tTd(34, 11)) 102565152Seric printf("\n"); 102665152Seric 102768449Seric if (bitset(H_STRIPVAL, h->h_flags)) 10289382Seric { 102968449Seric /* empty field */ 103068449Seric (void) sprintf(obuf, "%s:", h->h_field); 103168449Seric putline(obuf, mci); 103268449Seric } 103368449Seric else if (bitset(H_FROM|H_RCPT, h->h_flags)) 103468449Seric { 10359382Seric /* address field */ 10369382Seric bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 10379382Seric 10389382Seric if (bitset(H_FROM, h->h_flags)) 10399382Seric oldstyle = FALSE; 104065870Seric commaize(h, p, oldstyle, mci, e); 10419382Seric } 10429382Seric else 10439382Seric { 10449382Seric /* vanilla header line */ 104512159Seric register char *nlp; 104612159Seric 104759579Seric (void) sprintf(obuf, "%s: ", h->h_field); 104856795Seric while ((nlp = strchr(p, '\n')) != NULL) 104912159Seric { 105012159Seric *nlp = '\0'; 105112159Seric (void) strcat(obuf, p); 105212159Seric *nlp = '\n'; 105365870Seric putline(obuf, mci); 105412159Seric p = ++nlp; 105512161Seric obuf[0] = '\0'; 105612159Seric } 105712159Seric (void) strcat(obuf, p); 105865870Seric putline(obuf, mci); 10599382Seric } 10609382Seric } 106167887Seric 106267887Seric /* 106367887Seric ** If we are converting this to a MIME message, add the 106467889Seric ** MIME headers. 106567887Seric */ 106667887Seric 106767887Seric if (bitset(MM_MIME8BIT, MimeMode) && 106867887Seric bitset(EF_HAS8BIT, e->e_flags) && 106967887Seric !bitnset(M_8BITS, mci->mci_mailer->m_flags) && 107067889Seric !bitset(MCIF_CVT8TO7, mci->mci_flags)) 107167887Seric { 107267889Seric if (hvalue("MIME-Version", e->e_header) == NULL) 107367889Seric putline("MIME-Version: 1.0", mci); 107467889Seric if (hvalue("Content-Type", e->e_header) == NULL) 107567889Seric { 107667889Seric sprintf(obuf, "Content-Type: text/plain; charset=%s", 107767896Seric defcharset(e)); 107867889Seric putline(obuf, mci); 107967889Seric } 108067889Seric if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL) 108167889Seric putline("Content-Transfer-Encoding: 8bit", mci); 108267887Seric } 10839382Seric } 10849382Seric /* 10859382Seric ** COMMAIZE -- output a header field, making a comma-translated list. 10869382Seric ** 10879382Seric ** Parameters: 10889382Seric ** h -- the header field to output. 10899382Seric ** p -- the value to put in it. 10909382Seric ** oldstyle -- TRUE if this is an old style header. 109165870Seric ** mci -- the connection information. 109255012Seric ** e -- the envelope containing the message. 10939382Seric ** 10949382Seric ** Returns: 10959382Seric ** none. 10969382Seric ** 10979382Seric ** Side Effects: 10989382Seric ** outputs "p" to file "fp". 10999382Seric */ 11009382Seric 110165870Seric void 110265870Seric commaize(h, p, oldstyle, mci, e) 11039382Seric register HDR *h; 11049382Seric register char *p; 11059382Seric bool oldstyle; 110665870Seric register MCI *mci; 110755012Seric register ENVELOPE *e; 11089382Seric { 11099382Seric register char *obp; 11109382Seric int opos; 111166004Seric int omax; 11129382Seric bool firstone = TRUE; 111311157Seric char obuf[MAXLINE + 3]; 11149382Seric 11159382Seric /* 11169382Seric ** Output the address list translated by the 11179382Seric ** mailer and with commas. 11189382Seric */ 11199382Seric 11209382Seric if (tTd(14, 2)) 11219382Seric printf("commaize(%s: %s)\n", h->h_field, p); 11229382Seric 11239382Seric obp = obuf; 112459579Seric (void) sprintf(obp, "%s: ", h->h_field); 11259382Seric opos = strlen(h->h_field) + 2; 11269382Seric obp += opos; 112766254Seric omax = mci->mci_mailer->m_linelimit - 2; 112866254Seric if (omax < 0 || omax > 78) 112966254Seric omax = 78; 11309382Seric 11319382Seric /* 11329382Seric ** Run through the list of values. 11339382Seric */ 11349382Seric 11359382Seric while (*p != '\0') 11369382Seric { 11379382Seric register char *name; 113854983Seric register int c; 11399382Seric char savechar; 114059163Seric int flags; 114159163Seric auto int stat; 11429382Seric 11439382Seric /* 11449382Seric ** Find the end of the name. New style names 11459382Seric ** end with a comma, old style names end with 11469382Seric ** a space character. However, spaces do not 11479382Seric ** necessarily delimit an old-style name -- at 11489382Seric ** signs mean keep going. 11499382Seric */ 11509382Seric 11519382Seric /* find end of name */ 115258050Seric while ((isascii(*p) && isspace(*p)) || *p == ',') 11539382Seric p++; 11549382Seric name = p; 11559382Seric for (;;) 11569382Seric { 115758333Seric auto char *oldp; 115816909Seric char pvpbuf[PSBUFSIZE]; 11599382Seric 116065066Seric (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf, 116168711Seric sizeof pvpbuf, &oldp, NULL); 116258333Seric p = oldp; 11639382Seric 11649382Seric /* look to see if we have an at sign */ 116558050Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 11669382Seric p++; 11679382Seric 116858170Seric if (*p != '@') 11699382Seric { 11709382Seric p = oldp; 11719382Seric break; 11729382Seric } 11739382Seric p += *p == '@' ? 1 : 2; 117458050Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 11759382Seric p++; 11769382Seric } 11779382Seric /* at the end of one complete name */ 11789382Seric 11799382Seric /* strip off trailing white space */ 118058050Seric while (p >= name && 118158050Seric ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0')) 11829382Seric p--; 11839382Seric if (++p == name) 11849382Seric continue; 11859382Seric savechar = *p; 11869382Seric *p = '\0'; 11879382Seric 11889382Seric /* translate the name to be relative */ 118959163Seric flags = RF_HEADERADDR|RF_ADDDOMAIN; 119059163Seric if (bitset(H_FROM, h->h_flags)) 119159163Seric flags |= RF_SENDERADDR; 119268522Seric #ifdef USERDB 119368522Seric else if (e->e_from.q_mailer != NULL && 119468522Seric bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags)) 119568522Seric { 119668522Seric extern char *udbsender(); 119768522Seric 119868522Seric name = udbsender(name); 119968522Seric } 120068522Seric #endif 120159163Seric stat = EX_OK; 120265870Seric name = remotename(name, mci->mci_mailer, flags, &stat, e); 12039382Seric if (*name == '\0') 12049382Seric { 12059382Seric *p = savechar; 12069382Seric continue; 12079382Seric } 12089382Seric 12099382Seric /* output the name with nice formatting */ 121054983Seric opos += strlen(name); 12119382Seric if (!firstone) 12129382Seric opos += 2; 121366004Seric if (opos > omax && !firstone) 12149382Seric { 121510178Seric (void) strcpy(obp, ",\n"); 121665870Seric putline(obuf, mci); 12179382Seric obp = obuf; 121866255Seric (void) strcpy(obp, " "); 121910161Seric opos = strlen(obp); 122010161Seric obp += opos; 122154983Seric opos += strlen(name); 12229382Seric } 12239382Seric else if (!firstone) 12249382Seric { 122566255Seric (void) strcpy(obp, ", "); 12269382Seric obp += 2; 12279382Seric } 12289382Seric 122954983Seric while ((c = *name++) != '\0' && obp < &obuf[MAXLINE]) 123054983Seric *obp++ = c; 12319382Seric firstone = FALSE; 12329382Seric *p = savechar; 12339382Seric } 12349382Seric (void) strcpy(obp, "\n"); 123565870Seric putline(obuf, mci); 12369382Seric } 12379382Seric /* 123858170Seric ** COPYHEADER -- copy header list 12399382Seric ** 124058170Seric ** This routine is the equivalent of newstr for header lists 124158170Seric ** 12429382Seric ** Parameters: 124358170Seric ** header -- list of header structures to copy. 12449382Seric ** 12459382Seric ** Returns: 124658170Seric ** a copy of 'header'. 12479382Seric ** 12489382Seric ** Side Effects: 12499382Seric ** none. 12509382Seric */ 12519382Seric 125258170Seric HDR * 125358170Seric copyheader(header) 125458170Seric register HDR *header; 12559382Seric { 125658170Seric register HDR *newhdr; 125758170Seric HDR *ret; 125858170Seric register HDR **tail = &ret; 12599382Seric 126058170Seric while (header != NULL) 126158170Seric { 126258170Seric newhdr = (HDR *) xalloc(sizeof(HDR)); 126358170Seric STRUCTCOPY(*header, *newhdr); 126458170Seric *tail = newhdr; 126558170Seric tail = &newhdr->h_link; 126658170Seric header = header->h_link; 126758170Seric } 126858170Seric *tail = NULL; 126958170Seric 127058170Seric return ret; 12719382Seric } 1272