122976Smiriam /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333730Sbostic * Copyright (c) 1988 Regents of the University of California. 433730Sbostic * All rights reserved. 533730Sbostic * 6*42828Sbostic * %sccs.include.redist.c% 733730Sbostic */ 822976Smiriam 922976Smiriam #ifndef lint 10*42828Sbostic static char sccsid[] = "@(#)parseaddr.c 5.13 (Berkeley) 06/01/90"; 1133730Sbostic #endif /* not lint */ 1222976Smiriam 133312Seric # include "sendmail.h" 14297Seric 15297Seric /* 169888Seric ** PARSEADDR -- Parse an address 17297Seric ** 18297Seric ** Parses an address and breaks it up into three parts: a 19297Seric ** net to transmit the message on, the host to transmit it 20297Seric ** to, and a user on that host. These are loaded into an 212973Seric ** ADDRESS header with the values squirreled away if necessary. 22297Seric ** The "user" part may not be a real user; the process may 23297Seric ** just reoccur on that machine. For example, on a machine 24297Seric ** with an arpanet connection, the address 25297Seric ** csvax.bill@berkeley 26297Seric ** will break up to a "user" of 'csvax.bill' and a host 27297Seric ** of 'berkeley' -- to be transmitted over the arpanet. 28297Seric ** 29297Seric ** Parameters: 30297Seric ** addr -- the address to parse. 31297Seric ** a -- a pointer to the address descriptor buffer. 32297Seric ** If NULL, a header will be created. 33297Seric ** copyf -- determines what shall be copied: 34297Seric ** -1 -- don't copy anything. The printname 35297Seric ** (q_paddr) is just addr, and the 36297Seric ** user & host are allocated internally 37297Seric ** to parse. 38297Seric ** 0 -- copy out the parsed user & host, but 39297Seric ** don't copy the printname. 40297Seric ** +1 -- copy everything. 4111445Seric ** delim -- the character to terminate the address, passed 4211445Seric ** to prescan. 43297Seric ** 44297Seric ** Returns: 45297Seric ** A pointer to the address descriptor header (`a' if 46297Seric ** `a' is non-NULL). 47297Seric ** NULL on error. 48297Seric ** 49297Seric ** Side Effects: 50297Seric ** none 51297Seric */ 52297Seric 539374Seric /* following delimiters are inherent to the internal algorithms */ 5416155Seric # define DELIMCHARS "\001()<>,;\\\"\r\n" /* word delimiters */ 552091Seric 562973Seric ADDRESS * 5711445Seric parseaddr(addr, a, copyf, delim) 58297Seric char *addr; 592973Seric register ADDRESS *a; 60297Seric int copyf; 6111445Seric char delim; 62297Seric { 633149Seric register char **pvp; 643149Seric register struct mailer *m; 6516914Seric char pvpbuf[PSBUFSIZE]; 663149Seric extern char **prescan(); 673149Seric extern ADDRESS *buildaddr(); 68297Seric 69297Seric /* 70297Seric ** Initialize and prescan address. 71297Seric */ 72297Seric 736903Seric CurEnv->e_to = addr; 747675Seric if (tTd(20, 1)) 759888Seric printf("\n--parseaddr(%s)\n", addr); 763188Seric 7716914Seric pvp = prescan(addr, delim, pvpbuf); 783149Seric if (pvp == NULL) 79297Seric return (NULL); 80297Seric 81297Seric /* 823149Seric ** Apply rewriting rules. 837889Seric ** Ruleset 0 does basic parsing. It must resolve. 84297Seric */ 85297Seric 868181Seric rewrite(pvp, 3); 874070Seric rewrite(pvp, 0); 88297Seric 893149Seric /* 903149Seric ** See if we resolved to a real mailer. 913149Seric */ 92297Seric 933149Seric if (pvp[0][0] != CANONNET) 943149Seric { 953149Seric setstat(EX_USAGE); 963149Seric usrerr("cannot resolve name"); 973149Seric return (NULL); 98297Seric } 99297Seric 100297Seric /* 1013149Seric ** Build canonical address from pvp. 102297Seric */ 103297Seric 1043149Seric a = buildaddr(pvp, a); 1054279Seric if (a == NULL) 1064279Seric return (NULL); 1074598Seric m = a->q_mailer; 108297Seric 109297Seric /* 1103149Seric ** Make local copies of the host & user and then 1113149Seric ** transport them out. 112297Seric */ 113297Seric 114297Seric if (copyf > 0) 1158078Seric { 1168078Seric extern char *DelimChar; 1178078Seric char savec = *DelimChar; 1188078Seric 1198078Seric *DelimChar = '\0'; 1202973Seric a->q_paddr = newstr(addr); 1218078Seric *DelimChar = savec; 1228078Seric } 123297Seric else 124297Seric a->q_paddr = addr; 12524944Seric 12624944Seric if (a->q_user == NULL) 12724944Seric a->q_user = ""; 12824944Seric if (a->q_host == NULL) 12924944Seric a->q_host = ""; 13024944Seric 1313149Seric if (copyf >= 0) 132297Seric { 13324944Seric a->q_host = newstr(a->q_host); 1343149Seric if (a->q_user != a->q_paddr) 1353149Seric a->q_user = newstr(a->q_user); 136297Seric } 137297Seric 138297Seric /* 13916202Seric ** Convert host name to lower case if requested. 14016202Seric ** User name will be done later. 14116202Seric */ 14216202Seric 14316202Seric if (!bitnset(M_HST_UPPER, m->m_flags)) 14416202Seric makelower(a->q_host); 14516202Seric 14616202Seric /* 147297Seric ** Compute return value. 148297Seric */ 149297Seric 1507675Seric if (tTd(20, 1)) 1514443Seric { 1529888Seric printf("parseaddr-->"); 1534443Seric printaddr(a, FALSE); 1544443Seric } 155297Seric 156297Seric return (a); 157297Seric } 158297Seric /* 15916162Seric ** LOWERADDR -- map UPPER->lower case on addresses as requested. 16016162Seric ** 16116162Seric ** Parameters: 16216162Seric ** a -- address to be mapped. 16316162Seric ** 16416162Seric ** Returns: 16516162Seric ** none. 16616162Seric ** 16716162Seric ** Side Effects: 16816162Seric ** none. 16916162Seric */ 17016162Seric 17116162Seric loweraddr(a) 17216162Seric register ADDRESS *a; 17316162Seric { 17416162Seric register MAILER *m = a->q_mailer; 17516162Seric 17616162Seric if (!bitnset(M_USR_UPPER, m->m_flags)) 17716162Seric makelower(a->q_user); 17816162Seric } 17916162Seric /* 180297Seric ** PRESCAN -- Prescan name and make it canonical 181297Seric ** 1829374Seric ** Scans a name and turns it into a set of tokens. This process 1839374Seric ** deletes blanks and comments (in parentheses). 184297Seric ** 185297Seric ** This routine knows about quoted strings and angle brackets. 186297Seric ** 187297Seric ** There are certain subtleties to this routine. The one that 188297Seric ** comes to mind now is that backslashes on the ends of names 189297Seric ** are silently stripped off; this is intentional. The problem 190297Seric ** is that some versions of sndmsg (like at LBL) set the kill 191297Seric ** character to something other than @ when reading addresses; 192297Seric ** so people type "csvax.eric\@berkeley" -- which screws up the 193297Seric ** berknet mailer. 194297Seric ** 195297Seric ** Parameters: 196297Seric ** addr -- the name to chomp. 197297Seric ** delim -- the delimiter for the address, normally 198297Seric ** '\0' or ','; \0 is accepted in any case. 19915284Seric ** If '\t' then we are reading the .cf file. 20016914Seric ** pvpbuf -- place to put the saved text -- note that 20116914Seric ** the pointers are static. 202297Seric ** 203297Seric ** Returns: 2043149Seric ** A pointer to a vector of tokens. 205297Seric ** NULL on error. 206297Seric ** 207297Seric ** Side Effects: 20825279Seric ** sets DelimChar to point to the character matching 'delim'. 209297Seric */ 210297Seric 2118078Seric /* states and character types */ 2128078Seric # define OPR 0 /* operator */ 2138078Seric # define ATM 1 /* atom */ 2148078Seric # define QST 2 /* in quoted string */ 2158078Seric # define SPC 3 /* chewing up spaces */ 2168078Seric # define ONE 4 /* pick up one character */ 2173149Seric 2188078Seric # define NSTATES 5 /* number of states */ 2198078Seric # define TYPE 017 /* mask to select state type */ 2208078Seric 2218078Seric /* meta bits for table */ 2228078Seric # define M 020 /* meta character; don't pass through */ 2238078Seric # define B 040 /* cause a break */ 2248078Seric # define MB M|B /* meta-break */ 2258078Seric 2268078Seric static short StateTab[NSTATES][NSTATES] = 2278078Seric { 2288087Seric /* oldst chtype> OPR ATM QST SPC ONE */ 2299051Seric /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, 2309051Seric /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B, 2319051Seric /*QST*/ QST, QST, OPR, QST, QST, 2328078Seric /*SPC*/ OPR, ATM, QST, SPC|M, ONE, 2338078Seric /*ONE*/ OPR, OPR, OPR, OPR, OPR, 2348078Seric }; 2358078Seric 2368078Seric # define NOCHAR -1 /* signal nothing in lookahead token */ 2378078Seric 2388078Seric char *DelimChar; /* set to point to the delimiter */ 2398078Seric 2403149Seric char ** 24116914Seric prescan(addr, delim, pvpbuf) 242297Seric char *addr; 243297Seric char delim; 24416914Seric char pvpbuf[]; 245297Seric { 246297Seric register char *p; 2478078Seric register char *q; 2489346Seric register int c; 2493149Seric char **avp; 250297Seric bool bslashmode; 251297Seric int cmntcnt; 2528423Seric int anglecnt; 2533149Seric char *tok; 2548078Seric int state; 2558078Seric int newstate; 2568078Seric static char *av[MAXATOM+1]; 25715253Seric extern int errno; 258297Seric 25915253Seric /* make sure error messages don't have garbage on them */ 26015253Seric errno = 0; 26115253Seric 26216914Seric q = pvpbuf; 2633149Seric bslashmode = FALSE; 2647800Seric cmntcnt = 0; 2658423Seric anglecnt = 0; 2663149Seric avp = av; 2678078Seric state = OPR; 2688078Seric c = NOCHAR; 2698078Seric p = addr; 2708078Seric if (tTd(22, 45)) 271297Seric { 2728078Seric printf("prescan: "); 2738078Seric xputs(p); 27423109Seric (void) putchar('\n'); 2758078Seric } 2768078Seric 2778078Seric do 2788078Seric { 2793149Seric /* read a token */ 2803149Seric tok = q; 2818078Seric for (;;) 282297Seric { 2838078Seric /* store away any old lookahead character */ 2848078Seric if (c != NOCHAR) 2858078Seric { 28615284Seric /* see if there is room */ 28716914Seric if (q >= &pvpbuf[PSBUFSIZE - 5]) 2888078Seric { 2898078Seric usrerr("Address too long"); 2908078Seric DelimChar = p; 2918078Seric return (NULL); 2928078Seric } 29315284Seric 29415284Seric /* squirrel it away */ 2958078Seric *q++ = c; 2968078Seric } 2978078Seric 2988078Seric /* read a new input character */ 2998078Seric c = *p++; 3008078Seric if (c == '\0') 3018078Seric break; 30215284Seric c &= ~0200; 30315284Seric 3048078Seric if (tTd(22, 101)) 3058078Seric printf("c=%c, s=%d; ", c, state); 3068078Seric 3073149Seric /* chew up special characters */ 3083149Seric *q = '\0'; 3093149Seric if (bslashmode) 3103149Seric { 31124944Seric /* kludge \! for naive users */ 31224944Seric if (c != '!') 31324944Seric c |= 0200; 3143149Seric bslashmode = FALSE; 3153149Seric } 3163149Seric else if (c == '\\') 3173149Seric { 3183149Seric bslashmode = TRUE; 3198078Seric c = NOCHAR; 3203149Seric } 3218514Seric else if (state == QST) 3228514Seric { 3238514Seric /* do nothing, just avoid next clauses */ 3248514Seric } 3258078Seric else if (c == '(') 3264100Seric { 3278078Seric cmntcnt++; 3288078Seric c = NOCHAR; 3294100Seric } 3308078Seric else if (c == ')') 3313149Seric { 3328078Seric if (cmntcnt <= 0) 3333149Seric { 3348078Seric usrerr("Unbalanced ')'"); 3358078Seric DelimChar = p; 3368078Seric return (NULL); 3373149Seric } 3388078Seric else 3398078Seric cmntcnt--; 3408078Seric } 3418078Seric else if (cmntcnt > 0) 3428078Seric c = NOCHAR; 3438423Seric else if (c == '<') 3448423Seric anglecnt++; 3458423Seric else if (c == '>') 3468423Seric { 3478423Seric if (anglecnt <= 0) 3488423Seric { 3498423Seric usrerr("Unbalanced '>'"); 3508423Seric DelimChar = p; 3518423Seric return (NULL); 3528423Seric } 3538423Seric anglecnt--; 3548423Seric } 35511423Seric else if (delim == ' ' && isspace(c)) 35611423Seric c = ' '; 3573149Seric 3588078Seric if (c == NOCHAR) 3598078Seric continue; 3603149Seric 3618078Seric /* see if this is end of input */ 36211405Seric if (c == delim && anglecnt <= 0 && state != QST) 3633149Seric break; 3643149Seric 3658078Seric newstate = StateTab[state][toktype(c)]; 3668078Seric if (tTd(22, 101)) 3678078Seric printf("ns=%02o\n", newstate); 3688078Seric state = newstate & TYPE; 3698078Seric if (bitset(M, newstate)) 3708078Seric c = NOCHAR; 3718078Seric if (bitset(B, newstate)) 3724228Seric break; 373297Seric } 3743149Seric 3753149Seric /* new token */ 3768078Seric if (tok != q) 3771378Seric { 3788078Seric *q++ = '\0'; 3798078Seric if (tTd(22, 36)) 380297Seric { 3818078Seric printf("tok="); 3828078Seric xputs(tok); 38323109Seric (void) putchar('\n'); 384297Seric } 3858078Seric if (avp >= &av[MAXATOM]) 386297Seric { 3878078Seric syserr("prescan: too many tokens"); 3888078Seric DelimChar = p; 3898078Seric return (NULL); 390297Seric } 3918078Seric *avp++ = tok; 392297Seric } 3938423Seric } while (c != '\0' && (c != delim || anglecnt > 0)); 3943149Seric *avp = NULL; 3958078Seric DelimChar = --p; 3963149Seric if (cmntcnt > 0) 3973149Seric usrerr("Unbalanced '('"); 3988423Seric else if (anglecnt > 0) 3998423Seric usrerr("Unbalanced '<'"); 4008078Seric else if (state == QST) 4013149Seric usrerr("Unbalanced '\"'"); 4023149Seric else if (av[0] != NULL) 4033149Seric return (av); 4043149Seric return (NULL); 4053149Seric } 4063149Seric /* 4073149Seric ** TOKTYPE -- return token type 4083149Seric ** 4093149Seric ** Parameters: 4103149Seric ** c -- the character in question. 4113149Seric ** 4123149Seric ** Returns: 4133149Seric ** Its type. 4143149Seric ** 4153149Seric ** Side Effects: 4163149Seric ** none. 4173149Seric */ 418297Seric 4193149Seric toktype(c) 4203149Seric register char c; 4213149Seric { 4223380Seric static char buf[50]; 4233382Seric static bool firstime = TRUE; 4243380Seric 4253382Seric if (firstime) 4263380Seric { 4273382Seric firstime = FALSE; 42816155Seric expand("\001o", buf, &buf[sizeof buf - 1], CurEnv); 4297005Seric (void) strcat(buf, DELIMCHARS); 4303380Seric } 4319585Seric if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS) 4328078Seric return (ONE); 4338078Seric if (c == '"') 4348078Seric return (QST); 4354100Seric if (!isascii(c)) 4368078Seric return (ATM); 4378078Seric if (isspace(c) || c == ')') 4388078Seric return (SPC); 4393380Seric if (iscntrl(c) || index(buf, c) != NULL) 4408078Seric return (OPR); 4418078Seric return (ATM); 4423149Seric } 4433149Seric /* 4443149Seric ** REWRITE -- apply rewrite rules to token vector. 4453149Seric ** 4464476Seric ** This routine is an ordered production system. Each rewrite 4474476Seric ** rule has a LHS (called the pattern) and a RHS (called the 4484476Seric ** rewrite); 'rwr' points the the current rewrite rule. 4494476Seric ** 4504476Seric ** For each rewrite rule, 'avp' points the address vector we 4514476Seric ** are trying to match against, and 'pvp' points to the pattern. 4528058Seric ** If pvp points to a special match value (MATCHZANY, MATCHANY, 4539585Seric ** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp 4549585Seric ** matched is saved away in the match vector (pointed to by 'mvp'). 4554476Seric ** 4564476Seric ** When a match between avp & pvp does not match, we try to 4579585Seric ** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS 4584476Seric ** we must also back out the match in mvp. If we reach a 4598058Seric ** MATCHANY or MATCHZANY we just extend the match and start 4608058Seric ** over again. 4614476Seric ** 4624476Seric ** When we finally match, we rewrite the address vector 4634476Seric ** and try over again. 4644476Seric ** 4653149Seric ** Parameters: 4663149Seric ** pvp -- pointer to token vector. 4673149Seric ** 4683149Seric ** Returns: 4693149Seric ** none. 4703149Seric ** 4713149Seric ** Side Effects: 4723149Seric ** pvp is modified. 4733149Seric */ 4742091Seric 4753149Seric struct match 4763149Seric { 4774468Seric char **first; /* first token matched */ 4784468Seric char **last; /* last token matched */ 4793149Seric }; 4803149Seric 4814468Seric # define MAXMATCH 9 /* max params per rewrite */ 4823149Seric 4833149Seric 4844070Seric rewrite(pvp, ruleset) 4853149Seric char **pvp; 4864070Seric int ruleset; 4873149Seric { 4883149Seric register char *ap; /* address pointer */ 4893149Seric register char *rp; /* rewrite pointer */ 4903149Seric register char **avp; /* address vector pointer */ 4913149Seric register char **rvp; /* rewrite vector pointer */ 4928058Seric register struct match *mlp; /* cur ptr into mlist */ 4938058Seric register struct rewrite *rwr; /* pointer to current rewrite rule */ 4944468Seric struct match mlist[MAXMATCH]; /* stores match on LHS */ 4953149Seric char *npvp[MAXATOM+1]; /* temporary space for rebuild */ 4963149Seric 4979279Seric if (OpMode == MD_TEST || tTd(21, 2)) 4983149Seric { 4998959Seric printf("rewrite: ruleset %2d input:", ruleset); 5003149Seric printav(pvp); 5013149Seric } 5028423Seric if (pvp == NULL) 5038423Seric return; 5043149Seric 5053149Seric /* 5063149Seric ** Run through the list of rewrite rules, applying 5073149Seric ** any that match. 5083149Seric */ 5093149Seric 5104070Seric for (rwr = RewriteRules[ruleset]; rwr != NULL; ) 5113149Seric { 5127675Seric if (tTd(21, 12)) 513297Seric { 5148069Seric printf("-----trying rule:"); 5153149Seric printav(rwr->r_lhs); 5163149Seric } 5173149Seric 5183149Seric /* try to match on this rule */ 5194468Seric mlp = mlist; 5208058Seric rvp = rwr->r_lhs; 5218058Seric avp = pvp; 5228058Seric while ((ap = *avp) != NULL || *rvp != NULL) 5233149Seric { 5243149Seric rp = *rvp; 5258058Seric if (tTd(21, 35)) 5268058Seric { 5278069Seric printf("ap="); 5288058Seric xputs(ap); 5298069Seric printf(", rp="); 5308058Seric xputs(rp); 5318069Seric printf("\n"); 5328058Seric } 5333149Seric if (rp == NULL) 534297Seric { 5353149Seric /* end-of-pattern before end-of-address */ 5368058Seric goto backup; 537297Seric } 5388058Seric if (ap == NULL && *rp != MATCHZANY) 5398058Seric { 5408058Seric /* end-of-input */ 5418058Seric break; 5428058Seric } 5433149Seric 5443149Seric switch (*rp) 5453149Seric { 5464060Seric register STAB *s; 5474060Seric 5484060Seric case MATCHCLASS: 5499585Seric case MATCHNCLASS: 5509585Seric /* match any token in (not in) a class */ 5514100Seric s = stab(ap, ST_CLASS, ST_FIND); 55210690Seric if (s == NULL || !bitnset(rp[1], s->s_class)) 5539585Seric { 5549585Seric if (*rp == MATCHCLASS) 5559585Seric goto backup; 5569585Seric } 5579585Seric else if (*rp == MATCHNCLASS) 5588058Seric goto backup; 5594468Seric 5604476Seric /* explicit fall-through */ 5614476Seric 5624476Seric case MATCHONE: 5634476Seric case MATCHANY: 5644476Seric /* match exactly one token */ 5658058Seric mlp->first = avp; 5668058Seric mlp->last = avp++; 5674468Seric mlp++; 5684060Seric break; 5694060Seric 5708058Seric case MATCHZANY: 5718058Seric /* match zero or more tokens */ 5728058Seric mlp->first = avp; 5738058Seric mlp->last = avp - 1; 5748058Seric mlp++; 5758058Seric break; 5768058Seric 5773149Seric default: 5783149Seric /* must have exact match */ 57933725Sbostic if (strcasecmp(rp, ap)) 5808058Seric goto backup; 5814468Seric avp++; 5823149Seric break; 5833149Seric } 5843149Seric 5853149Seric /* successful match on this token */ 5863149Seric rvp++; 5873149Seric continue; 5883149Seric 5898058Seric backup: 5903149Seric /* match failed -- back up */ 5913149Seric while (--rvp >= rwr->r_lhs) 5923149Seric { 5933149Seric rp = *rvp; 5948058Seric if (*rp == MATCHANY || *rp == MATCHZANY) 5954468Seric { 5964476Seric /* extend binding and continue */ 5978058Seric avp = ++mlp[-1].last; 5988058Seric avp++; 5994476Seric rvp++; 6003149Seric break; 6014468Seric } 6024476Seric avp--; 6039585Seric if (*rp == MATCHONE || *rp == MATCHCLASS || 6049585Seric *rp == MATCHNCLASS) 6053149Seric { 6064468Seric /* back out binding */ 6074468Seric mlp--; 6083149Seric } 6093149Seric } 6103149Seric 6113149Seric if (rvp < rwr->r_lhs) 6123149Seric { 6133149Seric /* total failure to match */ 6143149Seric break; 6153149Seric } 616297Seric } 6173149Seric 6183149Seric /* 6193149Seric ** See if we successfully matched 6203149Seric */ 6213149Seric 6229374Seric if (rvp < rwr->r_lhs || *rvp != NULL) 6233149Seric { 6249374Seric if (tTd(21, 10)) 6259374Seric printf("----- rule fails\n"); 6269374Seric rwr = rwr->r_next; 6279374Seric continue; 6289374Seric } 6293149Seric 6309374Seric rvp = rwr->r_rhs; 6319374Seric if (tTd(21, 12)) 6329374Seric { 6339374Seric printf("-----rule matches:"); 6349374Seric printav(rvp); 6359374Seric } 6369374Seric 6379374Seric rp = *rvp; 6389374Seric if (*rp == CANONUSER) 6399374Seric { 6409374Seric rvp++; 6419374Seric rwr = rwr->r_next; 6429374Seric } 6439374Seric else if (*rp == CANONHOST) 6449374Seric { 6459374Seric rvp++; 6469374Seric rwr = NULL; 6479374Seric } 6489374Seric else if (*rp == CANONNET) 6499374Seric rwr = NULL; 6509374Seric 6519374Seric /* substitute */ 6529374Seric for (avp = npvp; *rvp != NULL; rvp++) 6539374Seric { 6549374Seric register struct match *m; 6559374Seric register char **pp; 6569374Seric 6578058Seric rp = *rvp; 65816914Seric if (*rp == MATCHREPL) 6598058Seric { 66016914Seric /* substitute from LHS */ 66116914Seric m = &mlist[rp[1] - '1']; 66216914Seric if (m >= mlp) 6639374Seric { 66416914Seric syserr("rewrite: ruleset %d: replacement out of bounds", ruleset); 6659374Seric return; 6669374Seric } 66716914Seric if (tTd(21, 15)) 66816914Seric { 66916914Seric printf("$%c:", rp[1]); 67016914Seric pp = m->first; 67116914Seric while (pp <= m->last) 67216914Seric { 67316914Seric printf(" %x=\"", *pp); 67416914Seric (void) fflush(stdout); 67516914Seric printf("%s\"", *pp++); 67616914Seric } 67716914Seric printf("\n"); 67816914Seric } 6799374Seric pp = m->first; 6809374Seric while (pp <= m->last) 6813149Seric { 68216914Seric if (avp >= &npvp[MAXATOM]) 68316914Seric { 68416914Seric syserr("rewrite: expansion too long"); 68516914Seric return; 68616914Seric } 68716914Seric *avp++ = *pp++; 6883149Seric } 6893149Seric } 69016914Seric else 6918226Seric { 69216914Seric /* vanilla replacement */ 6939374Seric if (avp >= &npvp[MAXATOM]) 69416889Seric { 69516914Seric toolong: 69616889Seric syserr("rewrite: expansion too long"); 69716889Seric return; 69816889Seric } 69916914Seric *avp++ = rp; 7008226Seric } 7019374Seric } 7029374Seric *avp++ = NULL; 70316914Seric 70416914Seric /* 70516914Seric ** Check for any hostname lookups. 70616914Seric */ 70716914Seric 70816914Seric for (rvp = npvp; *rvp != NULL; rvp++) 70916914Seric { 71016914Seric char **hbrvp; 71116914Seric char **xpvp; 71216914Seric int trsize; 71317473Seric char *olddelimchar; 71416920Seric char buf[MAXNAME + 1]; 71516914Seric char *pvpb1[MAXATOM + 1]; 71617174Seric char pvpbuf[PSBUFSIZE]; 71717473Seric extern char *DelimChar; 71816914Seric 71916914Seric if (**rvp != HOSTBEGIN) 72016914Seric continue; 72116914Seric 72216914Seric /* 72316914Seric ** Got a hostname lookup. 72416914Seric ** 72516914Seric ** This could be optimized fairly easily. 72616914Seric */ 72716914Seric 72816914Seric hbrvp = rvp; 72916914Seric 73016914Seric /* extract the match part */ 73116914Seric while (*++rvp != NULL && **rvp != HOSTEND) 73216914Seric continue; 73316914Seric if (*rvp != NULL) 73416914Seric *rvp++ = NULL; 73516914Seric 73616914Seric /* save the remainder of the input string */ 73716914Seric trsize = (int) (avp - rvp + 1) * sizeof *rvp; 73816914Seric bcopy((char *) rvp, (char *) pvpb1, trsize); 73916914Seric 74016914Seric /* look it up */ 74116914Seric cataddr(++hbrvp, buf, sizeof buf); 74216914Seric maphostname(buf, sizeof buf); 74316914Seric 74416914Seric /* scan the new host name */ 74517473Seric olddelimchar = DelimChar; 74616914Seric xpvp = prescan(buf, '\0', pvpbuf); 74717473Seric DelimChar = olddelimchar; 74816914Seric if (xpvp == NULL) 74916914Seric { 75016914Seric syserr("rewrite: cannot prescan canonical hostname: %s", buf); 75122976Smiriam return; 75216914Seric } 75316914Seric 75416914Seric /* append it to the token list */ 75517174Seric for (avp = --hbrvp; *xpvp != NULL; xpvp++) 75617174Seric { 75717174Seric *avp++ = newstr(*xpvp); 75816920Seric if (avp >= &npvp[MAXATOM]) 75916914Seric goto toolong; 76017174Seric } 76116914Seric 76216914Seric /* restore the old trailing information */ 76317177Seric for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) 76416920Seric if (avp >= &npvp[MAXATOM]) 76516914Seric goto toolong; 76617174Seric 76717174Seric break; 76816914Seric } 76916914Seric 77016914Seric /* 77116914Seric ** Check for subroutine calls. 77216914Seric */ 77316914Seric 77424944Seric if (*npvp != NULL && **npvp == CALLSUBR) 7759374Seric { 77616889Seric bcopy((char *) &npvp[2], (char *) pvp, 77716900Seric (int) (avp - npvp - 2) * sizeof *avp); 77816889Seric if (tTd(21, 3)) 77916889Seric printf("-----callsubr %s\n", npvp[1]); 78016889Seric rewrite(pvp, atoi(npvp[1])); 7813149Seric } 7823149Seric else 7833149Seric { 78417348Seric bcopy((char *) npvp, (char *) pvp, 78516900Seric (int) (avp - npvp) * sizeof *avp); 7869374Seric } 7879374Seric if (tTd(21, 4)) 7889374Seric { 7899374Seric printf("rewritten as:"); 7909374Seric printav(pvp); 7919374Seric } 792297Seric } 7938069Seric 7949279Seric if (OpMode == MD_TEST || tTd(21, 2)) 7958069Seric { 7968959Seric printf("rewrite: ruleset %2d returns:", ruleset); 7978069Seric printav(pvp); 7988069Seric } 7993149Seric } 8003149Seric /* 8013149Seric ** BUILDADDR -- build address from token vector. 8023149Seric ** 8033149Seric ** Parameters: 8043149Seric ** tv -- token vector. 8053149Seric ** a -- pointer to address descriptor to fill. 8063149Seric ** If NULL, one will be allocated. 8073149Seric ** 8083149Seric ** Returns: 8094279Seric ** NULL if there was an error. 8104279Seric ** 'a' otherwise. 8113149Seric ** 8123149Seric ** Side Effects: 8133149Seric ** fills in 'a' 8143149Seric */ 8153149Seric 8163149Seric ADDRESS * 8173149Seric buildaddr(tv, a) 8183149Seric register char **tv; 8193149Seric register ADDRESS *a; 8203149Seric { 8213149Seric static char buf[MAXNAME]; 8223149Seric struct mailer **mp; 8233149Seric register struct mailer *m; 8243149Seric 8253149Seric if (a == NULL) 8263149Seric a = (ADDRESS *) xalloc(sizeof *a); 82716889Seric bzero((char *) a, sizeof *a); 8283149Seric 8293149Seric /* figure out what net/mailer to use */ 8303149Seric if (**tv != CANONNET) 8314279Seric { 8323149Seric syserr("buildaddr: no net"); 8334279Seric return (NULL); 8344279Seric } 8353149Seric tv++; 83633725Sbostic if (!strcasecmp(*tv, "error")) 8374279Seric { 83810183Seric if (**++tv == CANONHOST) 83910183Seric { 84010183Seric setstat(atoi(*++tv)); 84110183Seric tv++; 84210183Seric } 84310183Seric if (**tv != CANONUSER) 8444279Seric syserr("buildaddr: error: no user"); 8454279Seric buf[0] = '\0'; 8464279Seric while (*++tv != NULL) 8474279Seric { 8484279Seric if (buf[0] != '\0') 8497005Seric (void) strcat(buf, " "); 8507005Seric (void) strcat(buf, *tv); 8514279Seric } 8524279Seric usrerr(buf); 8534279Seric return (NULL); 8544279Seric } 8554598Seric for (mp = Mailer; (m = *mp++) != NULL; ) 8563149Seric { 85733725Sbostic if (!strcasecmp(m->m_name, *tv)) 8583149Seric break; 8593149Seric } 8603149Seric if (m == NULL) 8614279Seric { 86224944Seric syserr("buildaddr: unknown mailer %s", *tv); 8634279Seric return (NULL); 8644279Seric } 8654598Seric a->q_mailer = m; 8663149Seric 8673149Seric /* figure out what host (if any) */ 8683149Seric tv++; 86910690Seric if (!bitnset(M_LOCAL, m->m_flags)) 8703149Seric { 8715704Seric if (**tv++ != CANONHOST) 8724279Seric { 8733149Seric syserr("buildaddr: no host"); 8744279Seric return (NULL); 8754279Seric } 8765704Seric buf[0] = '\0'; 8775704Seric while (*tv != NULL && **tv != CANONUSER) 8787005Seric (void) strcat(buf, *tv++); 8795704Seric a->q_host = newstr(buf); 8803149Seric } 8813149Seric else 8823149Seric a->q_host = NULL; 8833149Seric 8843149Seric /* figure out the user */ 88536615Sbostic if (*tv == NULL || **tv != CANONUSER) 8864279Seric { 8873149Seric syserr("buildaddr: no user"); 8884279Seric return (NULL); 8894279Seric } 89019040Seric 89119040Seric /* rewrite according recipient mailer rewriting rules */ 89219040Seric rewrite(++tv, 2); 89319040Seric if (m->m_r_rwset > 0) 89419040Seric rewrite(tv, m->m_r_rwset); 89519040Seric rewrite(tv, 4); 89619040Seric 89719040Seric /* save the result for the command line/RCPT argument */ 89811278Seric cataddr(tv, buf, sizeof buf); 8993149Seric a->q_user = buf; 9003149Seric 9013149Seric return (a); 9023149Seric } 9033188Seric /* 9044228Seric ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 9054228Seric ** 9064228Seric ** Parameters: 9074228Seric ** pvp -- parameter vector to rebuild. 9084228Seric ** buf -- buffer to build the string into. 9094228Seric ** sz -- size of buf. 9104228Seric ** 9114228Seric ** Returns: 9124228Seric ** none. 9134228Seric ** 9144228Seric ** Side Effects: 9154228Seric ** Destroys buf. 9164228Seric */ 9174228Seric 9184228Seric cataddr(pvp, buf, sz) 9194228Seric char **pvp; 9204228Seric char *buf; 9214228Seric register int sz; 9224228Seric { 9234228Seric bool oatomtok = FALSE; 9244228Seric bool natomtok = FALSE; 9254228Seric register int i; 9264228Seric register char *p; 9274228Seric 9288423Seric if (pvp == NULL) 9298423Seric { 93023109Seric (void) strcpy(buf, ""); 9318423Seric return; 9328423Seric } 9334228Seric p = buf; 93411156Seric sz -= 2; 9354228Seric while (*pvp != NULL && (i = strlen(*pvp)) < sz) 9364228Seric { 9378078Seric natomtok = (toktype(**pvp) == ATM); 9384228Seric if (oatomtok && natomtok) 9399042Seric *p++ = SpaceSub; 9404228Seric (void) strcpy(p, *pvp); 9414228Seric oatomtok = natomtok; 9424228Seric p += i; 94311156Seric sz -= i + 1; 9444228Seric pvp++; 9454228Seric } 9464228Seric *p = '\0'; 9474228Seric } 9484228Seric /* 9493188Seric ** SAMEADDR -- Determine if two addresses are the same 9503188Seric ** 9513188Seric ** This is not just a straight comparison -- if the mailer doesn't 9523188Seric ** care about the host we just ignore it, etc. 9533188Seric ** 9543188Seric ** Parameters: 9553188Seric ** a, b -- pointers to the internal forms to compare. 9563188Seric ** 9573188Seric ** Returns: 9583188Seric ** TRUE -- they represent the same mailbox. 9593188Seric ** FALSE -- they don't. 9603188Seric ** 9613188Seric ** Side Effects: 9623188Seric ** none. 9633188Seric */ 9643188Seric 9653188Seric bool 9669374Seric sameaddr(a, b) 9673188Seric register ADDRESS *a; 9683188Seric register ADDRESS *b; 9693188Seric { 9703188Seric /* if they don't have the same mailer, forget it */ 9713188Seric if (a->q_mailer != b->q_mailer) 9723188Seric return (FALSE); 9733188Seric 9743188Seric /* if the user isn't the same, we can drop out */ 9759374Seric if (strcmp(a->q_user, b->q_user) != 0) 9763188Seric return (FALSE); 9773188Seric 9783188Seric /* if the mailer ignores hosts, we have succeeded! */ 97910690Seric if (bitnset(M_LOCAL, a->q_mailer->m_flags)) 9803188Seric return (TRUE); 9813188Seric 9823188Seric /* otherwise compare hosts (but be careful for NULL ptrs) */ 9833188Seric if (a->q_host == NULL || b->q_host == NULL) 9843188Seric return (FALSE); 9853188Seric if (strcmp(a->q_host, b->q_host) != 0) 9863188Seric return (FALSE); 9873188Seric 9883188Seric return (TRUE); 9893188Seric } 9903234Seric /* 9913234Seric ** PRINTADDR -- print address (for debugging) 9923234Seric ** 9933234Seric ** Parameters: 9943234Seric ** a -- the address to print 9953234Seric ** follow -- follow the q_next chain. 9963234Seric ** 9973234Seric ** Returns: 9983234Seric ** none. 9993234Seric ** 10003234Seric ** Side Effects: 10013234Seric ** none. 10023234Seric */ 10033234Seric 10043234Seric printaddr(a, follow) 10053234Seric register ADDRESS *a; 10063234Seric bool follow; 10073234Seric { 10085001Seric bool first = TRUE; 10095001Seric 10103234Seric while (a != NULL) 10113234Seric { 10125001Seric first = FALSE; 10134443Seric printf("%x=", a); 10144085Seric (void) fflush(stdout); 101540973Sbostic printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n", 101640973Sbostic a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name, 101740973Sbostic a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>"); 10188181Seric printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags, 10198181Seric a->q_alias); 10208181Seric printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home, 10218181Seric a->q_fullname); 10224996Seric 10233234Seric if (!follow) 10243234Seric return; 10254996Seric a = a->q_next; 10263234Seric } 10275001Seric if (first) 10284443Seric printf("[NULL]\n"); 10293234Seric } 10304317Seric 10317682Seric /* 10327682Seric ** REMOTENAME -- return the name relative to the current mailer 10337682Seric ** 10347682Seric ** Parameters: 10357682Seric ** name -- the name to translate. 10368069Seric ** m -- the mailer that we want to do rewriting relative 10378069Seric ** to. 10388069Seric ** senderaddress -- if set, uses the sender rewriting rules 10398069Seric ** rather than the recipient rewriting rules. 104010310Seric ** canonical -- if set, strip out any comment information, 104110310Seric ** etc. 10427682Seric ** 10437682Seric ** Returns: 10447682Seric ** the text string representing this address relative to 10457682Seric ** the receiving mailer. 10467682Seric ** 10477682Seric ** Side Effects: 10487682Seric ** none. 10497682Seric ** 10507682Seric ** Warnings: 10517682Seric ** The text string returned is tucked away locally; 10527682Seric ** copy it if you intend to save it. 10537682Seric */ 10547682Seric 10557682Seric char * 105610310Seric remotename(name, m, senderaddress, canonical) 10577682Seric char *name; 10587682Seric struct mailer *m; 10598069Seric bool senderaddress; 106010310Seric bool canonical; 10617682Seric { 10628069Seric register char **pvp; 10638069Seric char *fancy; 10648069Seric extern char *macvalue(); 10658181Seric char *oldg = macvalue('g', CurEnv); 10667682Seric static char buf[MAXNAME]; 10677682Seric char lbuf[MAXNAME]; 106816914Seric char pvpbuf[PSBUFSIZE]; 10697682Seric extern char **prescan(); 10707889Seric extern char *crackaddr(); 10717682Seric 10727755Seric if (tTd(12, 1)) 10737755Seric printf("remotename(%s)\n", name); 10747755Seric 107510177Seric /* don't do anything if we are tagging it as special */ 107610177Seric if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0) 107710177Seric return (name); 107810177Seric 10797682Seric /* 10808181Seric ** Do a heuristic crack of this name to extract any comment info. 10818181Seric ** This will leave the name as a comment and a $g macro. 10827889Seric */ 10837889Seric 108410310Seric if (canonical) 108516155Seric fancy = "\001g"; 108610310Seric else 108710310Seric fancy = crackaddr(name); 10887889Seric 10898181Seric /* 10908181Seric ** Turn the name into canonical form. 10918181Seric ** Normally this will be RFC 822 style, i.e., "user@domain". 10928181Seric ** If this only resolves to "user", and the "C" flag is 10938181Seric ** specified in the sending mailer, then the sender's 10948181Seric ** domain will be appended. 10958181Seric */ 10968181Seric 109716914Seric pvp = prescan(name, '\0', pvpbuf); 10987889Seric if (pvp == NULL) 10997889Seric return (name); 11008181Seric rewrite(pvp, 3); 11018181Seric if (CurEnv->e_fromdomain != NULL) 11028181Seric { 11038181Seric /* append from domain to this address */ 11048181Seric register char **pxp = pvp; 11058181Seric 11069594Seric /* see if there is an "@domain" in the current name */ 11078181Seric while (*pxp != NULL && strcmp(*pxp, "@") != 0) 11088181Seric pxp++; 11098181Seric if (*pxp == NULL) 11108181Seric { 11119594Seric /* no.... append the "@domain" from the sender */ 11128181Seric register char **qxq = CurEnv->e_fromdomain; 11138181Seric 11149594Seric while ((*pxp++ = *qxq++) != NULL) 11159594Seric continue; 111611726Seric rewrite(pvp, 3); 11178181Seric } 11188181Seric } 11198181Seric 11208181Seric /* 11218959Seric ** Do more specific rewriting. 11228181Seric ** Rewrite using ruleset 1 or 2 depending on whether this is 11238181Seric ** a sender address or not. 11248181Seric ** Then run it through any receiving-mailer-specific rulesets. 11258181Seric */ 11268181Seric 11278069Seric if (senderaddress) 11287755Seric { 11297889Seric rewrite(pvp, 1); 11308069Seric if (m->m_s_rwset > 0) 11318069Seric rewrite(pvp, m->m_s_rwset); 11328069Seric } 11338069Seric else 11348069Seric { 11357889Seric rewrite(pvp, 2); 11368069Seric if (m->m_r_rwset > 0) 11378069Seric rewrite(pvp, m->m_r_rwset); 11387682Seric } 11397682Seric 11408181Seric /* 11418959Seric ** Do any final sanitation the address may require. 11428959Seric ** This will normally be used to turn internal forms 11438959Seric ** (e.g., user@host.LOCAL) into external form. This 11448959Seric ** may be used as a default to the above rules. 11458959Seric */ 11468959Seric 11478959Seric rewrite(pvp, 4); 11488959Seric 11498959Seric /* 11508181Seric ** Now restore the comment information we had at the beginning. 11518181Seric */ 11528181Seric 11537682Seric cataddr(pvp, lbuf, sizeof lbuf); 11549374Seric define('g', lbuf, CurEnv); 11557889Seric expand(fancy, buf, &buf[sizeof buf - 1], CurEnv); 11569374Seric define('g', oldg, CurEnv); 11577682Seric 11587682Seric if (tTd(12, 1)) 11597755Seric printf("remotename => `%s'\n", buf); 11607682Seric return (buf); 11617682Seric } 1162