122976Smiriam /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333730Sbostic * Copyright (c) 1988 Regents of the University of California. 433730Sbostic * All rights reserved. 533730Sbostic * 642828Sbostic * %sccs.include.redist.c% 733730Sbostic */ 822976Smiriam 922976Smiriam #ifndef lint 10*56327Seric static char sccsid[] = "@(#)parseaddr.c 5.13.1.2 (Berkeley) 09/22/92"; 1133730Sbostic #endif /* not lint */ 1222976Smiriam 1356326Seric #include "sendmail.h" 14297Seric 1556326Seric #ifdef CC_WONT_PROMOTE 1656326Seric static int toktype __P((char)); 1756326Seric #else /* !CC_WONT_PROMOTE */ 1856326Seric static int toktype __P((int)); /* char -> int */ 1956326Seric #endif /* CC_WONT_PROMOTE */ 2056326Seric static void _rewrite __P((char **, int)); 2156326Seric static void callsubr __P((char **)); 2256326Seric static ADDRESS * buildaddr __P((char **, ADDRESS *)); 2356326Seric static void uurelativize __P((const char *, const char *, char **)); 2456326Seric 2556326Seric char *DelimChar; /* set to point to the delimiter */ 2656326Seric 27297Seric /* 289888Seric ** PARSEADDR -- Parse an address 29297Seric ** 30297Seric ** Parses an address and breaks it up into three parts: a 31297Seric ** net to transmit the message on, the host to transmit it 32297Seric ** to, and a user on that host. These are loaded into an 332973Seric ** ADDRESS header with the values squirreled away if necessary. 34297Seric ** The "user" part may not be a real user; the process may 35297Seric ** just reoccur on that machine. For example, on a machine 36297Seric ** with an arpanet connection, the address 37297Seric ** csvax.bill@berkeley 38297Seric ** will break up to a "user" of 'csvax.bill' and a host 39297Seric ** of 'berkeley' -- to be transmitted over the arpanet. 40297Seric ** 41297Seric ** Parameters: 42297Seric ** addr -- the address to parse. 43297Seric ** a -- a pointer to the address descriptor buffer. 44297Seric ** If NULL, a header will be created. 45297Seric ** copyf -- determines what shall be copied: 46297Seric ** -1 -- don't copy anything. The printname 47297Seric ** (q_paddr) is just addr, and the 48297Seric ** user & host are allocated internally 49297Seric ** to parse. 50297Seric ** 0 -- copy out the parsed user & host, but 51297Seric ** don't copy the printname. 52297Seric ** +1 -- copy everything. 5311445Seric ** delim -- the character to terminate the address, passed 5411445Seric ** to prescan. 55297Seric ** 56297Seric ** Returns: 57297Seric ** A pointer to the address descriptor header (`a' if 58297Seric ** `a' is non-NULL). 59297Seric ** NULL on error. 60297Seric ** 61297Seric ** Side Effects: 62297Seric ** none 63297Seric */ 64297Seric 659374Seric /* following delimiters are inherent to the internal algorithms */ 6616155Seric # define DELIMCHARS "\001()<>,;\\\"\r\n" /* word delimiters */ 672091Seric 682973Seric ADDRESS * 6956326Seric parseaddr(addr, a, copyf, delim) 70297Seric char *addr; 712973Seric register ADDRESS *a; 72297Seric int copyf; 7311445Seric char delim; 74297Seric { 753149Seric register char **pvp; 7656326Seric register struct mailer *m; 7716914Seric char pvpbuf[PSBUFSIZE]; 78297Seric 79297Seric /* 80297Seric ** Initialize and prescan address. 81297Seric */ 82297Seric 8356326Seric CurEnv->e_to = (char *)addr; 847675Seric if (tTd(20, 1)) 859888Seric printf("\n--parseaddr(%s)\n", addr); 863188Seric 8756326Seric { 8856326Seric extern char *DelimChar; /* parseaddr.c */ 8956326Seric char savec; 9056326Seric bool invalid; 91*56327Seric extern char *finddelim(); 92*56327Seric extern bool invalidaddr(); 9356326Seric 9456326Seric DelimChar = finddelim(addr, delim); 9556326Seric savec = *DelimChar; 9656326Seric *DelimChar = '\0'; 9756326Seric invalid = invalidaddr(addr); 9856326Seric *DelimChar = savec; 9956326Seric if (invalid) 10056326Seric return (NULL); 10156326Seric } 10256326Seric 10316914Seric pvp = prescan(addr, delim, pvpbuf); 1043149Seric if (pvp == NULL) 105297Seric return (NULL); 106297Seric 107297Seric /* 1083149Seric ** Apply rewriting rules. 1097889Seric ** Ruleset 0 does basic parsing. It must resolve. 110297Seric */ 111297Seric 1128181Seric rewrite(pvp, 3); 1134070Seric rewrite(pvp, 0); 114297Seric 1153149Seric /* 1163149Seric ** See if we resolved to a real mailer. 1173149Seric */ 118297Seric 1193149Seric if (pvp[0][0] != CANONNET) 1203149Seric { 1213149Seric setstat(EX_USAGE); 1223149Seric usrerr("cannot resolve name"); 1233149Seric return (NULL); 124297Seric } 125297Seric 126297Seric /* 1273149Seric ** Build canonical address from pvp. 128297Seric */ 129297Seric 1303149Seric a = buildaddr(pvp, a); 1314279Seric if (a == NULL) 1324279Seric return (NULL); 13356326Seric m = a->q_mailer; 134297Seric 135297Seric /* 1363149Seric ** Make local copies of the host & user and then 1373149Seric ** transport them out. 138297Seric */ 139297Seric 14056326Seric if (copyf > 0) 1418078Seric { 1428078Seric char savec = *DelimChar; 1438078Seric 1448078Seric *DelimChar = '\0'; 14556326Seric a->q_paddr = newstr(addr); 1468078Seric *DelimChar = savec; 1478078Seric } 148297Seric else 14956326Seric a->q_paddr = addr; 15024944Seric 15124944Seric if (a->q_user == NULL) 15224944Seric a->q_user = ""; 15324944Seric if (a->q_host == NULL) 15424944Seric a->q_host = ""; 15524944Seric 1563149Seric if (copyf >= 0) 157297Seric { 15824944Seric a->q_host = newstr(a->q_host); 1593149Seric if (a->q_user != a->q_paddr) 1603149Seric a->q_user = newstr(a->q_user); 161297Seric } 162297Seric 163297Seric /* 16416202Seric ** Convert host name to lower case if requested. 16516202Seric ** User name will be done later. 16616202Seric */ 16716202Seric 16816202Seric if (!bitnset(M_HST_UPPER, m->m_flags)) 16916202Seric makelower(a->q_host); 17056326Seric 17156326Seric /* 17256326Seric ** Compute return value. 17356326Seric */ 17456326Seric 17556326Seric if (tTd(20, 1)) 17656326Seric { 17756326Seric printf("parseaddr-->"); 17856326Seric printaddr(a, FALSE); 17956326Seric } 18056326Seric 18156326Seric return (a); 182297Seric } 183297Seric /* 18416162Seric ** LOWERADDR -- map UPPER->lower case on addresses as requested. 18516162Seric ** 18616162Seric ** Parameters: 18716162Seric ** a -- address to be mapped. 18816162Seric ** 18916162Seric ** Returns: 19016162Seric ** none. 19116162Seric ** 19216162Seric ** Side Effects: 19316162Seric ** none. 19416162Seric */ 19516162Seric 19656326Seric void 19716162Seric loweraddr(a) 19816162Seric register ADDRESS *a; 19916162Seric { 20016162Seric register MAILER *m = a->q_mailer; 20116162Seric 20216162Seric if (!bitnset(M_USR_UPPER, m->m_flags)) 20316162Seric makelower(a->q_user); 20416162Seric } 20516162Seric /* 20656326Seric ** INVALIDADDR -- check an address string for invalid control characters. 20756326Seric ** 20856326Seric ** Parameters: 20956326Seric ** addr -- address string to be checked. 21056326Seric ** 21156326Seric ** Returns: 21256326Seric ** TRUE if address string could cause problems, FALSE o/w. 21356326Seric ** 21456326Seric ** Side Effects: 21556326Seric ** ExitStat may be changed and an error message generated. 21656326Seric */ 21756326Seric 21856326Seric bool 21956326Seric invalidaddr(addr) 22056326Seric const char *addr; 22156326Seric { 22256326Seric register const char *cp; 22356326Seric 22456326Seric /* make sure error messages don't have garbage on them */ 22556326Seric errno = 0; 22656326Seric 22756326Seric /* 22856326Seric ** Sendmail reserves characters 020 - 036 for rewriting rules 22956326Seric ** which can cause havoc (e.g. infinite rewriting loops) if 23056326Seric ** one shows up at the wrong time. If any of these characters 23156326Seric ** appear in an address, the address is deemed "invalid" and 23256326Seric ** an error message is generated. 23356326Seric */ 23456326Seric 23556326Seric for (cp = addr; *cp; cp++) 23656326Seric if ((*cp >= MATCHZANY && *cp <= HOSTEND) || *cp == '\001') 23756326Seric { 23856326Seric setstat(EX_USAGE); 23956326Seric usrerr("address contained invalid control char(s)"); 24056326Seric return (TRUE); 24156326Seric } 24256326Seric return (FALSE); 24356326Seric } 24456326Seric /* 245297Seric ** PRESCAN -- Prescan name and make it canonical 246297Seric ** 2479374Seric ** Scans a name and turns it into a set of tokens. This process 2489374Seric ** deletes blanks and comments (in parentheses). 249297Seric ** 250297Seric ** This routine knows about quoted strings and angle brackets. 251297Seric ** 252297Seric ** There are certain subtleties to this routine. The one that 253297Seric ** comes to mind now is that backslashes on the ends of names 254297Seric ** are silently stripped off; this is intentional. The problem 255297Seric ** is that some versions of sndmsg (like at LBL) set the kill 256297Seric ** character to something other than @ when reading addresses; 257297Seric ** so people type "csvax.eric\@berkeley" -- which screws up the 258297Seric ** berknet mailer. 259297Seric ** 260297Seric ** Parameters: 261297Seric ** addr -- the name to chomp. 262297Seric ** delim -- the delimiter for the address, normally 263297Seric ** '\0' or ','; \0 is accepted in any case. 26415284Seric ** If '\t' then we are reading the .cf file. 26516914Seric ** pvpbuf -- place to put the saved text -- note that 26616914Seric ** the pointers are static. 267297Seric ** 268297Seric ** Returns: 2693149Seric ** A pointer to a vector of tokens. 270297Seric ** NULL on error. 271297Seric ** 272297Seric ** Side Effects: 27325279Seric ** sets DelimChar to point to the character matching 'delim'. 274297Seric */ 275297Seric 2768078Seric /* states and character types */ 2778078Seric # define OPR 0 /* operator */ 2788078Seric # define ATM 1 /* atom */ 2798078Seric # define QST 2 /* in quoted string */ 2808078Seric # define SPC 3 /* chewing up spaces */ 2818078Seric # define ONE 4 /* pick up one character */ 2823149Seric 2838078Seric # define NSTATES 5 /* number of states */ 2848078Seric # define TYPE 017 /* mask to select state type */ 2858078Seric 2868078Seric /* meta bits for table */ 2878078Seric # define M 020 /* meta character; don't pass through */ 2888078Seric # define B 040 /* cause a break */ 2898078Seric # define MB M|B /* meta-break */ 2908078Seric 2918078Seric static short StateTab[NSTATES][NSTATES] = 2928078Seric { 2938087Seric /* oldst chtype> OPR ATM QST SPC ONE */ 2949051Seric /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, 2959051Seric /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B, 2969051Seric /*QST*/ QST, QST, OPR, QST, QST, 2978078Seric /*SPC*/ OPR, ATM, QST, SPC|M, ONE, 2988078Seric /*ONE*/ OPR, OPR, OPR, OPR, OPR, 2998078Seric }; 3008078Seric 3018078Seric # define NOCHAR -1 /* signal nothing in lookahead token */ 3028078Seric 3033149Seric char ** 30416914Seric prescan(addr, delim, pvpbuf) 305297Seric char *addr; 306297Seric char delim; 30716914Seric char pvpbuf[]; 308297Seric { 309297Seric register char *p; 3108078Seric register char *q; 3119346Seric register int c; 3123149Seric char **avp; 313297Seric bool bslashmode; 314297Seric int cmntcnt; 3158423Seric int anglecnt; 3163149Seric char *tok; 3178078Seric int state; 3188078Seric int newstate; 3198078Seric static char *av[MAXATOM+1]; 320297Seric 32115253Seric /* make sure error messages don't have garbage on them */ 32215253Seric errno = 0; 32315253Seric 32416914Seric q = pvpbuf; 3253149Seric bslashmode = FALSE; 3267800Seric cmntcnt = 0; 3278423Seric anglecnt = 0; 3283149Seric avp = av; 32956326Seric state = OPR; 3308078Seric c = NOCHAR; 3318078Seric p = addr; 3328078Seric if (tTd(22, 45)) 333297Seric { 3348078Seric printf("prescan: "); 3358078Seric xputs(p); 33623109Seric (void) putchar('\n'); 3378078Seric } 3388078Seric 3398078Seric do 3408078Seric { 3413149Seric /* read a token */ 3423149Seric tok = q; 3438078Seric for (;;) 344297Seric { 3458078Seric /* store away any old lookahead character */ 3468078Seric if (c != NOCHAR) 3478078Seric { 34815284Seric /* see if there is room */ 34916914Seric if (q >= &pvpbuf[PSBUFSIZE - 5]) 3508078Seric { 3518078Seric usrerr("Address too long"); 3528078Seric DelimChar = p; 3538078Seric return (NULL); 3548078Seric } 35515284Seric 35615284Seric /* squirrel it away */ 3578078Seric *q++ = c; 3588078Seric } 3598078Seric 3608078Seric /* read a new input character */ 3618078Seric c = *p++; 3628078Seric if (c == '\0') 3638078Seric break; 36456326Seric c &= 0177; 36515284Seric 3668078Seric if (tTd(22, 101)) 3678078Seric printf("c=%c, s=%d; ", c, state); 3688078Seric 3693149Seric /* chew up special characters */ 3703149Seric *q = '\0'; 3713149Seric if (bslashmode) 3723149Seric { 37324944Seric /* kludge \! for naive users */ 37424944Seric if (c != '!') 37556326Seric c |= 0200; 3763149Seric bslashmode = FALSE; 37756326Seric if (cmntcnt > 0) 37856326Seric c = NOCHAR; 3793149Seric } 38056326Seric else if (c == '\\') 3813149Seric { 3823149Seric bslashmode = TRUE; 3838078Seric c = NOCHAR; 3843149Seric } 38556326Seric if (state == QST) 3868514Seric { 3878514Seric /* do nothing, just avoid next clauses */ 3888514Seric } 3898078Seric else if (c == '(') 3904100Seric { 3918078Seric cmntcnt++; 3928078Seric c = NOCHAR; 3934100Seric } 3948078Seric else if (c == ')') 3953149Seric { 3968078Seric if (cmntcnt <= 0) 3973149Seric { 3988078Seric usrerr("Unbalanced ')'"); 3998078Seric DelimChar = p; 4008078Seric return (NULL); 4013149Seric } 4028078Seric else 4038078Seric cmntcnt--; 4048078Seric } 4058078Seric else if (cmntcnt > 0) 4068078Seric c = NOCHAR; 4078423Seric else if (c == '<') 4088423Seric anglecnt++; 4098423Seric else if (c == '>') 4108423Seric { 4118423Seric if (anglecnt <= 0) 4128423Seric { 4138423Seric usrerr("Unbalanced '>'"); 4148423Seric DelimChar = p; 4158423Seric return (NULL); 4168423Seric } 4178423Seric anglecnt--; 4188423Seric } 41911423Seric else if (delim == ' ' && isspace(c)) 42011423Seric c = ' '; 4213149Seric 42256326Seric else if (c == ';') /* semicolons are not tokens */ 42356326Seric c = NOCHAR; 42456326Seric 4258078Seric if (c == NOCHAR) 4268078Seric continue; 4273149Seric 4288078Seric /* see if this is end of input */ 42911405Seric if (c == delim && anglecnt <= 0 && state != QST) 4303149Seric break; 4313149Seric 4328078Seric newstate = StateTab[state][toktype(c)]; 4338078Seric if (tTd(22, 101)) 4348078Seric printf("ns=%02o\n", newstate); 4358078Seric state = newstate & TYPE; 4368078Seric if (bitset(M, newstate)) 4378078Seric c = NOCHAR; 4388078Seric if (bitset(B, newstate)) 4394228Seric break; 440297Seric } 4413149Seric 4423149Seric /* new token */ 4438078Seric if (tok != q) 4441378Seric { 4458078Seric *q++ = '\0'; 4468078Seric if (tTd(22, 36)) 447297Seric { 4488078Seric printf("tok="); 4498078Seric xputs(tok); 45023109Seric (void) putchar('\n'); 451297Seric } 4528078Seric if (avp >= &av[MAXATOM]) 453297Seric { 4548078Seric syserr("prescan: too many tokens"); 4558078Seric DelimChar = p; 4568078Seric return (NULL); 457297Seric } 4588078Seric *avp++ = tok; 459297Seric } 4608423Seric } while (c != '\0' && (c != delim || anglecnt > 0)); 4613149Seric *avp = NULL; 4628078Seric DelimChar = --p; 4633149Seric if (cmntcnt > 0) 4643149Seric usrerr("Unbalanced '('"); 4658423Seric else if (anglecnt > 0) 4668423Seric usrerr("Unbalanced '<'"); 4678078Seric else if (state == QST) 4683149Seric usrerr("Unbalanced '\"'"); 4693149Seric else if (av[0] != NULL) 4703149Seric return (av); 4713149Seric return (NULL); 4723149Seric } 4733149Seric /* 4743149Seric ** TOKTYPE -- return token type 4753149Seric ** 4763149Seric ** Parameters: 4773149Seric ** c -- the character in question. 4783149Seric ** 4793149Seric ** Returns: 4803149Seric ** Its type. 4813149Seric ** 4823149Seric ** Side Effects: 4833149Seric ** none. 4843149Seric */ 485297Seric 48656326Seric static int 4873149Seric toktype(c) 4883149Seric register char c; 4893149Seric { 4903380Seric static char buf[50]; 4913382Seric static bool firstime = TRUE; 4923380Seric 4933382Seric if (firstime) 4943380Seric { 4953382Seric firstime = FALSE; 49616155Seric expand("\001o", buf, &buf[sizeof buf - 1], CurEnv); 4977005Seric (void) strcat(buf, DELIMCHARS); 4983380Seric } 499*56327Seric if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS) 5008078Seric return (ONE); 50156326Seric #ifdef MACVALUE 50256326Seric if (c == MACVALUE) 50356326Seric return (ONE); 50456326Seric #endif /* MACVALUE */ 5058078Seric if (c == '"') 5068078Seric return (QST); 5074100Seric if (!isascii(c)) 5088078Seric return (ATM); 5098078Seric if (isspace(c) || c == ')') 5108078Seric return (SPC); 5113380Seric if (iscntrl(c) || index(buf, c) != NULL) 5128078Seric return (OPR); 5138078Seric return (ATM); 5143149Seric } 5153149Seric /* 5163149Seric ** REWRITE -- apply rewrite rules to token vector. 5173149Seric ** 5184476Seric ** This routine is an ordered production system. Each rewrite 5194476Seric ** rule has a LHS (called the pattern) and a RHS (called the 5204476Seric ** rewrite); 'rwr' points the the current rewrite rule. 5214476Seric ** 5224476Seric ** For each rewrite rule, 'avp' points the address vector we 5234476Seric ** are trying to match against, and 'pvp' points to the pattern. 5248058Seric ** If pvp points to a special match value (MATCHZANY, MATCHANY, 5259585Seric ** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp 5269585Seric ** matched is saved away in the match vector (pointed to by 'mvp'). 5274476Seric ** 5284476Seric ** When a match between avp & pvp does not match, we try to 5299585Seric ** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS 5304476Seric ** we must also back out the match in mvp. If we reach a 5318058Seric ** MATCHANY or MATCHZANY we just extend the match and start 5328058Seric ** over again. 5334476Seric ** 5344476Seric ** When we finally match, we rewrite the address vector 5354476Seric ** and try over again. 5364476Seric ** 5373149Seric ** Parameters: 5383149Seric ** pvp -- pointer to token vector. 5393149Seric ** 5403149Seric ** Returns: 5413149Seric ** none. 5423149Seric ** 5433149Seric ** Side Effects: 5443149Seric ** pvp is modified. 5453149Seric */ 5462091Seric 54756326Seric # define OP_NONZLEN 00001 54856326Seric # define OP_VARLEN 00002 54956326Seric # define OP_CLASS 00004 55056326Seric # define OP_EXACT 00010 55156326Seric 5523149Seric struct match 5533149Seric { 5544468Seric char **first; /* first token matched */ 5554468Seric char **last; /* last token matched */ 55656326Seric char **source; /* left hand source operand */ 55756326Seric char flags; /* attributes of this operator */ 5583149Seric }; 5593149Seric 5604468Seric # define MAXMATCH 9 /* max params per rewrite */ 56156326Seric # define MAX_CONTROL ' ' 5623149Seric 56356326Seric static char control_opts[MAX_CONTROL]; 5643149Seric 56556326Seric static char control_init_data[] = { 56656326Seric MATCHZANY, OP_VARLEN, 56756326Seric MATCHONE, OP_NONZLEN, 56856326Seric MATCHANY, OP_VARLEN|OP_NONZLEN, 56956326Seric #ifdef MACVALUE 57056326Seric MACVALUE, OP_EXACT, 57156326Seric #endif /* MACVALUE */ 57256326Seric MATCHNCLASS, OP_NONZLEN, 57356326Seric MATCHCLASS, OP_NONZLEN|OP_VARLEN|OP_CLASS, 57456326Seric }; 57556326Seric 57656326Seric static int nrw; 57756326Seric 57856326Seric void 5794070Seric rewrite(pvp, ruleset) 5803149Seric char **pvp; 5814070Seric int ruleset; 5823149Seric { 58356326Seric nrw = 0; 58456326Seric _rewrite(pvp, ruleset); 58556326Seric } 58656326Seric 58756326Seric static void 58856326Seric _rewrite(pvp, ruleset) 58956326Seric char **pvp; 59056326Seric int ruleset; 59156326Seric { 5923149Seric register char *ap; /* address pointer */ 5933149Seric register char *rp; /* rewrite pointer */ 5943149Seric register char **avp; /* address vector pointer */ 5953149Seric register char **rvp; /* rewrite vector pointer */ 5968058Seric register struct match *mlp; /* cur ptr into mlist */ 5978058Seric register struct rewrite *rwr; /* pointer to current rewrite rule */ 5983149Seric char *npvp[MAXATOM+1]; /* temporary space for rebuild */ 59956326Seric char tokbuf[MAXNAME+1]; /* for concatenated class tokens */ 60056326Seric int nloops, nmatches = 0; /* for looping rule checks */ 60156326Seric struct rewrite *prev_rwr; /* pointer to previous rewrite rule */ 60256326Seric struct match mlist[MAXMATCH+1]; /* stores match on LHS */ 60356326Seric struct match *old_mlp; /* to save our place */ 60456326Seric bool extend_match; /* extend existing match during backup */ 6053149Seric 6069279Seric if (OpMode == MD_TEST || tTd(21, 2)) 6073149Seric { 6088959Seric printf("rewrite: ruleset %2d input:", ruleset); 60956326Seric printcav(pvp); 6103149Seric } 6118423Seric if (pvp == NULL) 6128423Seric return; 6133149Seric 61456326Seric if (++nrw > 100) 61556326Seric { 61656326Seric char buf[MAXLINE]; 61756326Seric 61856326Seric buf[0] = buf[MAXLINE-1] = 0; 61956326Seric while (*pvp) 62056326Seric (void) strncat(buf, *pvp++, sizeof buf); 62156326Seric syserr("address causes rewrite loop: <%s>", buf); 62256326Seric return; 62356326Seric } 62456326Seric 62556326Seric /* Be sure to recognize first rule as new */ 62656326Seric prev_rwr = NULL; 62756326Seric 6283149Seric /* 62956326Seric ** Run through the list of rewrite rules, applying any that match. 6303149Seric */ 6313149Seric 6324070Seric for (rwr = RewriteRules[ruleset]; rwr != NULL; ) 6333149Seric { 6347675Seric if (tTd(21, 12)) 635297Seric { 6368069Seric printf("-----trying rule:"); 63756326Seric printcav(rwr->r_lhs); 6383149Seric } 6393149Seric 64056326Seric /* 64156326Seric ** Set up the match list. This is done once for each 64256326Seric ** rule. If a rule is used repeatedly, the list need not 64356326Seric ** be set up the next time. 64456326Seric */ 64556326Seric 64656326Seric if (rwr != prev_rwr) 64756326Seric { 64856326Seric prev_rwr = rwr; 64956326Seric for (rvp = rwr->r_lhs, mlp = mlist; 65056326Seric *rvp && (mlp < &mlist[MAXMATCH]); rvp++) 65156326Seric { 65256326Seric mlp->flags = ((unsigned char) **rvp >= MAX_CONTROL) ? 65356326Seric 0 : control_opts[**rvp] ; 65456326Seric if (mlp->flags) 65556326Seric { 65656326Seric mlp->source = rvp; 65756326Seric mlp++; 65856326Seric } 65956326Seric } 66056326Seric if (*rvp) 66156326Seric { 66256326Seric syserr("Too many variables on LHS in ruleset %d", ruleset); 66356326Seric return; 66456326Seric } 66556326Seric mlp->source = rvp; 66656326Seric 66756326Seric /* Make sure end marker is initialized */ 66856326Seric mlp->flags = 0; 66956326Seric } 67056326Seric 6713149Seric /* try to match on this rule */ 6724468Seric mlp = mlist; 67356326Seric 6748058Seric rvp = rwr->r_lhs; 6758058Seric avp = pvp; 67656326Seric nloops = 0; 67756326Seric extend_match = FALSE; 67856326Seric 6798058Seric while ((ap = *avp) != NULL || *rvp != NULL) 6803149Seric { 68156326Seric if (nloops++ > 400) 68252637Seric { 68356326Seric syserr("Looping on ruleset %d, rule %d", 68456326Seric ruleset, rwr-RewriteRules[ruleset]); 68556326Seric mlp = mlist - 1; /* force rule failure */ 68652637Seric break; 68752637Seric } 6883149Seric rp = *rvp; 68956326Seric 6908058Seric if (tTd(21, 35)) 6918058Seric { 69256326Seric printf("ap="); 6938058Seric xputs(ap); 69456326Seric printf(", rp="); 6958058Seric xputs(rp); 6968069Seric printf("\n"); 6978058Seric } 69856326Seric 69956326Seric if (extend_match) 70056326Seric extend_match = FALSE; 70156326Seric else 70256326Seric { 70356326Seric mlp->first = avp; 70456326Seric mlp->last = mlp->flags == 0 || (mlp->flags & OP_NONZLEN) ? 70556326Seric avp + 1 : avp; 70656326Seric } 70756326Seric 7083149Seric if (rp == NULL) 7093149Seric /* end-of-pattern before end-of-address */ 7108058Seric goto backup; 71156326Seric 71256326Seric /* Premature end of address */ 71356326Seric if (ap == NULL && avp != mlp->last) 71456326Seric goto backup; 71556326Seric 71656326Seric /* 71756326Seric ** Simplest case - exact token comparison between 71856326Seric ** pattern and address. Such a match is not saved 71956326Seric ** in mlp. 72056326Seric */ 72156326Seric 72256326Seric if (rvp < mlp->source) 72356326Seric { 72456326Seric if (ap == NULL || strcasecmp(ap, rp)) 72556326Seric goto backup; 72656326Seric rvp++; 72756326Seric avp++; 72856326Seric continue; 729297Seric } 73056326Seric 73156326Seric #ifdef MACVALUE 73256326Seric /* 73356326Seric ** This is special case handled. The match is exact, 73456326Seric ** but might span zero or more address tokens. The 73556326Seric ** result is saved in mlp. 73656326Seric */ 73756326Seric 73856326Seric if (*rp == MACVALUE) 7398058Seric { 74056326Seric int len; 74156326Seric rp = macvalue(rp[1], CurEnv); 74256326Seric 74356326Seric if (rp) 74456326Seric while (*rp) 74556326Seric { 74656326Seric if (*avp == NULL || strncasecmp(rp,*avp,len = strlen(*avp))) 74756326Seric goto backup; 74856326Seric rp += len; 74956326Seric avp++; 75056326Seric } 75156326Seric mlp->last = avp; 75256326Seric rvp++; 75356326Seric mlp++; 75456326Seric continue; 7558058Seric } 75656326Seric #endif /* MACVALUE */ 7573149Seric 75856326Seric /* 75956326Seric ** All other matches are saved in mlp. Initially 76056326Seric ** assume they match at the shortest possible length 76156326Seric ** for this pattern. Variable patterns will be 76256326Seric ** extended later as needed. 76356326Seric */ 76456326Seric 76556326Seric /* Fixed length first */ 76656326Seric if (!(mlp->flags & OP_VARLEN)) 7673149Seric { 76856326Seric switch (*rp) 76956326Seric { 77056326Seric register STAB *s; 7714060Seric 77256326Seric case MATCHNCLASS: 77356326Seric /* match any single token not in a class */ 77456326Seric s = stab(ap, ST_CLASS, ST_FIND); 77556326Seric if (s != NULL && bitnset(rp[1], s->s_class)) 7769585Seric goto backup; 77756326Seric break; 77856326Seric } 7794060Seric 78056326Seric avp = mlp->last; 78156326Seric rvp++; 7828058Seric mlp++; 78356326Seric continue; 78456326Seric } 7858058Seric 78656326Seric /* 78756326Seric ** We now have a variable length item. It could 78856326Seric ** be $+ or $* in which case no special checking 78956326Seric ** is needed. But a class match such as $=x must 79056326Seric ** be verified. 79156326Seric ** 79256326Seric ** As a speedup, if a variable length item is 79356326Seric ** followed by a plain character token, we initially 79456326Seric ** extend the match to the first such token we find. 79556326Seric ** If the required character token cannot be found, 79656326Seric ** we fail the match at this point. 79756326Seric */ 79856326Seric 79956326Seric avp = mlp->last; 80056326Seric 80156326Seric /* If next token is char token */ 80256326Seric if (&rvp[1] < mlp[1].source) 80356326Seric { 80456326Seric while (*avp && strcasecmp(*avp, rvp[1])) 80556326Seric avp++; 80656326Seric 80756326Seric /* 80856326Seric ** If we can't find the proper ending token, 80956326Seric ** leave avp point to NULL. This indicates 81056326Seric ** we have run out of address tokens. It is 81156326Seric ** pointless to advance the beginning of this 81256326Seric ** match and retry. 81356326Seric */ 81456326Seric 81556326Seric if (*avp == NULL) 8168058Seric goto backup; 81756326Seric mlp->last = avp; 81856326Seric } 81956326Seric else if (rvp[1] == NULL) 82056326Seric /* next token is end of address */ 82156326Seric { 82256326Seric while (*avp) 82356326Seric avp++; 82456326Seric mlp->last = avp; 82556326Seric } 82656326Seric 82756326Seric if (mlp->flags & OP_CLASS) 82856326Seric { 82956326Seric register char *cp = tokbuf; 83056326Seric 83156326Seric avp = mlp->first; 83256326Seric strcpy(cp, *avp); 8334468Seric avp++; 83456326Seric for (;;) 83556326Seric { 83656326Seric while (avp < mlp->last) 83756326Seric { 83856326Seric while (*cp) 83956326Seric cp++; 84056326Seric strcpy(cp, *avp); 84156326Seric avp++; 84256326Seric } 84356326Seric switch (*rp) 84456326Seric { 84556326Seric register STAB *s; 84656326Seric 84756326Seric case MATCHCLASS: 84856326Seric s = stab(tokbuf, ST_CLASS, ST_FIND); 84956326Seric if (s != NULL && bitnset(rp[1], s->s_class)) 85056326Seric goto have_match; 85156326Seric break; 85256326Seric } 85356326Seric 85456326Seric /* 85556326Seric ** Class match initially failed. 85656326Seric ** Extend the tentative match. 85756326Seric ** Again, if followed by a character 85856326Seric ** token, extend all the way to that 85956326Seric ** token before checking. 86056326Seric */ 86156326Seric 86256326Seric if (*avp) 86356326Seric { 86456326Seric (mlp->last)++; 86556326Seric if (&rvp[1] < mlp[1].source) 86656326Seric { 86756326Seric while (*(mlp->last) && strcasecmp(*(mlp->last), rvp[1])) 86856326Seric (mlp->last)++; 86956326Seric if (*(mlp->last) == NULL) 87056326Seric avp = mlp->last; 87156326Seric } 87256326Seric } 87356326Seric if (*avp == NULL) 87456326Seric { 87556326Seric /* 87656326Seric ** We could not find the 87756326Seric ** ending token. But we had 87856326Seric ** found ending tokens before. 87956326Seric ** A match is still plausible 88056326Seric ** if the start of the 88156326Seric ** tentative match is advanced. 88256326Seric ** Hence we must not leave avp 88356326Seric ** pointing to NULL. 88456326Seric */ 88556326Seric avp = mlp->first; 88656326Seric goto backup; 88756326Seric } 88856326Seric } 8893149Seric } 8903149Seric 89156326Seric have_match: 8923149Seric rvp++; 89356326Seric mlp++; 8943149Seric continue; 8953149Seric 89656326Seric backup: 89756326Seric /* We failed to match. mlp marks point of failure */ 89856326Seric 89956326Seric /* 90056326Seric ** There is a special case when we have exhausted 90156326Seric ** the address, but have not exhausted the pattern. 90256326Seric ** Under normal circumstances we could consider the 90356326Seric ** failure permanent, since extending the number of 90456326Seric ** address tokens matched by a '$+' or a '$*' will 90556326Seric ** only worsen the situation. 90656326Seric ** 90756326Seric ** There is an exception, however. It is possible 90856326Seric ** that we have matched a class token, say '$=x', 90956326Seric ** with three or more tokens. Extending a '$+' say, 91056326Seric ** which precedes the '$=x' will move the beginning 91156326Seric ** of the '$=x' match to the right, but it might match 91256326Seric ** a smaller number of tokens then, possibly 91356326Seric ** correcting the mismatch. 91456326Seric ** 91556326Seric ** Thus in this case we initially back up to the 91656326Seric ** $=x which matches three or more tokens. 91756326Seric */ 91856326Seric 91956326Seric if (*avp == NULL) 9203149Seric { 92156326Seric while (--mlp > mlist) 9224468Seric { 92356326Seric if ((mlp->flags & OP_CLASS) && 92456326Seric mlp->last > 2 + mlp->first) 92556326Seric break; 9264468Seric } 9273149Seric } 9283149Seric 92956326Seric /* 93056326Seric ** Now backup till we find a match with a pattern 93156326Seric ** whose length is extendable, and extend that. 93256326Seric */ 93356326Seric 93456326Seric mlp--; 93556326Seric while (mlp >= mlist && !(mlp->flags & OP_VARLEN)) 93656326Seric mlp--; 93756326Seric 93856326Seric /* Total failure to match */ 93956326Seric if (mlp < mlist) 94056326Seric break; 94156326Seric 94256326Seric avp = ++(mlp->last); 94356326Seric rvp = mlp->source; 94456326Seric 94556326Seric /* 94656326Seric ** We have found a backup point. Normally we would 94756326Seric ** increase the matched amount by one token, and 94856326Seric ** continue from the next item in the pattern. But 94956326Seric ** there are two special cases. If this is a 95056326Seric ** class-type match (OP_CLASS), we must test the 95156326Seric ** validity of the extended match. If this pattern 95256326Seric ** item is directly followed by a character token, it 95356326Seric ** is worth going back and locating the next such 95456326Seric ** character token before we continue on. 95556326Seric */ 95656326Seric if ((mlp->flags & OP_CLASS) || (&rvp[1] < mlp[1].source)) 9573149Seric { 95856326Seric avp = mlp->first; 95956326Seric extend_match = TRUE; 9603149Seric } 96156326Seric else 96256326Seric { 96356326Seric mlp++; 96456326Seric rvp++; 96556326Seric } 966297Seric } 9673149Seric 9683149Seric /* 96956326Seric ** See if we successfully matched. 9703149Seric */ 9713149Seric 97256326Seric if (mlp < mlist) 9733149Seric { 9749374Seric if (tTd(21, 10)) 9759374Seric printf("----- rule fails\n"); 9769374Seric rwr = rwr->r_next; 97756326Seric nmatches = 0; 9789374Seric continue; 9799374Seric } 9803149Seric 98156326Seric if (nmatches++ > 200) 98256326Seric { 98356326Seric syserr("Loop in ruleset %d, rule %d (too many matches)", 98456326Seric ruleset, rwr - RewriteRules[ruleset]); 98556326Seric rwr = rwr->r_next; 98656326Seric nmatches = 0; 98756326Seric continue; 98856326Seric } 98956326Seric 9909374Seric rvp = rwr->r_rhs; 9919374Seric if (tTd(21, 12)) 9929374Seric { 9939374Seric printf("-----rule matches:"); 99456326Seric printcav(rvp); 9959374Seric } 9969374Seric 9979374Seric rp = *rvp; 9989374Seric if (*rp == CANONUSER) 9999374Seric { 10009374Seric rvp++; 10019374Seric rwr = rwr->r_next; 100256326Seric nmatches = 0; 10039374Seric } 10049374Seric else if (*rp == CANONHOST) 10059374Seric { 10069374Seric rvp++; 10079374Seric rwr = NULL; 10089374Seric } 10099374Seric else if (*rp == CANONNET) 10109374Seric rwr = NULL; 10119374Seric 10129374Seric /* substitute */ 10139374Seric for (avp = npvp; *rvp != NULL; rvp++) 10149374Seric { 10159374Seric register struct match *m; 10169374Seric register char **pp; 10179374Seric 10188058Seric rp = *rvp; 101956326Seric if (*rp == MATCHREPL && rp[1] >= '1' && rp[1] <= '9') 10208058Seric { 102116914Seric /* substitute from LHS */ 102216914Seric m = &mlist[rp[1] - '1']; 102316914Seric if (m >= mlp) 10249374Seric { 102556326Seric syserr("rewrite: ruleset %d: replacement #%c out of bounds", 102656326Seric ruleset, rp[1]); 10279374Seric return; 10289374Seric } 102916914Seric if (tTd(21, 15)) 103016914Seric { 103116914Seric printf("$%c:", rp[1]); 103216914Seric pp = m->first; 103356326Seric while (pp < m->last) 103416914Seric { 103516914Seric printf(" %x=\"", *pp); 103616914Seric (void) fflush(stdout); 103716914Seric printf("%s\"", *pp++); 103816914Seric } 103916914Seric printf("\n"); 104016914Seric } 10419374Seric pp = m->first; 104256326Seric while (pp < m->last) 10433149Seric { 104416914Seric if (avp >= &npvp[MAXATOM]) 104556326Seric goto toolong; 104616914Seric *avp++ = *pp++; 10473149Seric } 10483149Seric } 104916914Seric else 10508226Seric { 105116914Seric /* vanilla replacement */ 10529374Seric if (avp >= &npvp[MAXATOM]) 105316889Seric { 105456326Seric toolong: 105516889Seric syserr("rewrite: expansion too long"); 105616889Seric return; 105716889Seric } 105856326Seric #ifdef MACVALUE 105956326Seric if (*rp == MACVALUE) 106056326Seric { 106156326Seric char *p = macvalue(rp[1], CurEnv); 106256326Seric 106356326Seric if (tTd(21, 2)) 106456326Seric printf("expanding runtime macro '%c' to \"%s\"\n", 106556326Seric rp[1], p ? p : "(null)"); 106656326Seric if (p) 106756326Seric *avp++ = p; 106856326Seric } 106956326Seric else 107056326Seric #endif /* MACVALUE */ 107156326Seric *avp++ = rp; 10728226Seric } 10739374Seric } 10749374Seric *avp++ = NULL; 107516914Seric 107616914Seric /* 107756326Seric ** Check for any hostname lookups. 107816914Seric */ 107916914Seric 108016914Seric for (rvp = npvp; *rvp != NULL; rvp++) 108116914Seric { 108256326Seric char **hbrvp, **ubrvp; 108316914Seric char **xpvp; 108416914Seric int trsize; 108517473Seric char *olddelimchar; 108656326Seric char hbuf[MAXNAME + 1], ubuf[MAXNAME + 1]; 108716914Seric char *pvpb1[MAXATOM + 1]; 108817174Seric char pvpbuf[PSBUFSIZE]; 108956326Seric bool match, defaultpart; 109056326Seric char begintype; 109156326Seric char db = '\0'; 109216914Seric 1093*56327Seric if (**rvp != HOSTBEGIN) 109416914Seric continue; 109516914Seric 109616914Seric /* 109756326Seric ** Got a hostname or database lookup. 109816914Seric ** 109916914Seric ** This could be optimized fairly easily. 110016914Seric */ 110116914Seric 110256326Seric begintype = **rvp; 110316914Seric hbrvp = rvp; 110456326Seric ubrvp = NULL; 110556326Seric 110616914Seric /* extract the match part */ 110756326Seric if (begintype == HOSTBEGIN) 1108*56327Seric { 110956326Seric while (*++rvp != NULL && **rvp != HOSTEND && 1110*56327Seric **rvp != CANONUSER) 1111*56327Seric continue; 1112*56327Seric } 111356326Seric else 1114*56327Seric { 1115*56327Seric while (*++rvp != NULL && **rvp != CANONHOST && 1116*56327Seric **rvp != CANONUSER) 111756326Seric continue; 1118*56327Seric } 111956326Seric /* got a sprintf argument? */ 112056326Seric if (**rvp == CANONHOST) 112153654Seric { 112256326Seric *rvp = NULL; 112356326Seric ubrvp = rvp+1; 1124*56327Seric while (*++rvp != NULL && **rvp != CANONUSER) 112556326Seric continue; 112653654Seric } 112756326Seric defaultpart = **rvp == CANONUSER; 112816914Seric if (*rvp != NULL) 112916914Seric *rvp++ = NULL; 113016914Seric 113116914Seric /* save the remainder of the input string */ 113216914Seric trsize = (int) (avp - rvp + 1) * sizeof *rvp; 113316914Seric bcopy((char *) rvp, (char *) pvpb1, trsize); 113416914Seric 113556326Seric /* Look it up (lowercase version) */ 113656326Seric cataddr(hbrvp + (begintype == HOSTBEGIN ? 1 : 2), 113756326Seric hbuf, sizeof hbuf); 113856326Seric if (begintype == HOSTBEGIN) 113956326Seric #ifdef VMUNIX 114056326Seric match = maphostname(hbuf, sizeof hbuf); 114156326Seric #else /* !VMUNIX */ 114256326Seric match = FALSE; 114356326Seric #endif /* VMUNIX */ 114453654Seric else 114551317Seric { 114656326Seric if (ubrvp == NULL) 114756326Seric { 114856326Seric /* no sprintf argument part */ 114956326Seric match = (mapkey(db, hbuf, sizeof hbuf, NULL) != NULL); 115056326Seric } 115153654Seric else 115256326Seric { 115356326Seric cataddr(ubrvp, ubuf, sizeof ubuf); 115456326Seric match = (mapkey(db, hbuf, sizeof hbuf, ubuf) != NULL); 115556326Seric } 115653654Seric } 115756326Seric if (match || !defaultpart) 115853654Seric { 115956326Seric /* scan the new route/host name */ 116056326Seric olddelimchar = DelimChar; 116156326Seric xpvp = prescan(hbuf, '\0', pvpbuf); 116256326Seric DelimChar = olddelimchar; 116353654Seric if (xpvp == NULL) 116451317Seric { 116556326Seric syserr("rewrite: cannot prescan %s: %s", 116656326Seric begintype == HOSTBEGIN ? 116756326Seric "new hostname" : 116856326Seric "dbm lookup result", 116956326Seric hbuf); 117056326Seric return; 117151317Seric } 117251317Seric 117316914Seric /* append it to the token list */ 117456326Seric for (avp = hbrvp; *xpvp != NULL; xpvp++) 117556326Seric { 117617174Seric *avp++ = newstr(*xpvp); 117716920Seric if (avp >= &npvp[MAXATOM]) 117816914Seric goto toolong; 117956326Seric } 118017174Seric } 118156326Seric else 118256326Seric avp = hbrvp; 118316914Seric 118416914Seric /* restore the old trailing information */ 118556326Seric rvp = avp - 1; 118656326Seric for (xpvp = pvpb1; *xpvp != NULL; xpvp++) 118756326Seric { 1188*56327Seric if (defaultpart && **xpvp == HOSTEND) 118956326Seric { 119056326Seric defaultpart = FALSE; 119156326Seric rvp = avp - 1; 119256326Seric } 119356326Seric else if (!defaultpart || !match) 119456326Seric *avp++ = *xpvp; 119516920Seric if (avp >= &npvp[MAXATOM]) 119616914Seric goto toolong; 119756326Seric } 119856326Seric *avp++ = NULL; 119917174Seric 120056326Seric /*break;*/ 120116914Seric } 120216914Seric 120316914Seric /* 120416914Seric ** Check for subroutine calls. 120556326Seric ** Then copy vector back into original space. 120616914Seric */ 120716914Seric 120856326Seric callsubr(npvp); 120956326Seric 121056326Seric for (avp = npvp; *avp++ != NULL;); 121117348Seric bcopy((char *) npvp, (char *) pvp, 121216900Seric (int) (avp - npvp) * sizeof *avp); 121356326Seric 12149374Seric if (tTd(21, 4)) 12159374Seric { 12169374Seric printf("rewritten as:"); 121756326Seric printcav(pvp); 12189374Seric } 1219297Seric } 12208069Seric 12219279Seric if (OpMode == MD_TEST || tTd(21, 2)) 12228069Seric { 12238959Seric printf("rewrite: ruleset %2d returns:", ruleset); 122456326Seric printcav(pvp); 12258069Seric } 12263149Seric } 12273149Seric /* 122856326Seric ** CALLSUBR -- call subroutines in rewrite vector 122956326Seric ** 123056326Seric ** Parameters: 123156326Seric ** pvp -- pointer to token vector. 123256326Seric ** 123356326Seric ** Returns: 123456326Seric ** none. 123556326Seric ** 123656326Seric ** Side Effects: 123756326Seric ** pvp is modified. 123856326Seric */ 123956326Seric 124056326Seric static void 124156326Seric callsubr(pvp) 124256326Seric char **pvp; 124356326Seric { 124456326Seric char **rvp; 124556326Seric int subr; 124656326Seric 124756326Seric for (; *pvp != NULL; pvp++) 124856326Seric if (**pvp == CALLSUBR && pvp[1] != NULL && isdigit(pvp[1][0])) 124956326Seric { 125056326Seric subr = atoi(pvp[1]); 125156326Seric 125256326Seric if (tTd(21, 3)) 125356326Seric printf("-----callsubr %d\n", subr); 125456326Seric 125556326Seric /* 125656326Seric ** Take care of possible inner calls. 125756326Seric */ 125856326Seric callsubr(pvp+2); 125956326Seric 126056326Seric /* 126156326Seric ** Move vector up over calling opcode. 126256326Seric */ 126356326Seric for (rvp = pvp+2; *rvp != NULL; rvp++) 126456326Seric rvp[-2] = rvp[0]; 126556326Seric rvp[-2] = NULL; 126656326Seric 126756326Seric /* 126856326Seric ** Call inferior ruleset. 126956326Seric */ 127056326Seric _rewrite(pvp, subr); 127156326Seric 127256326Seric break; 127356326Seric } 127456326Seric } 127556326Seric /* 12763149Seric ** BUILDADDR -- build address from token vector. 12773149Seric ** 12783149Seric ** Parameters: 12793149Seric ** tv -- token vector. 12803149Seric ** a -- pointer to address descriptor to fill. 12813149Seric ** If NULL, one will be allocated. 12823149Seric ** 12833149Seric ** Returns: 12844279Seric ** NULL if there was an error. 12854279Seric ** 'a' otherwise. 12863149Seric ** 12873149Seric ** Side Effects: 12883149Seric ** fills in 'a' 12893149Seric */ 12903149Seric 129156326Seric static ADDRESS * 12923149Seric buildaddr(tv, a) 12933149Seric register char **tv; 12943149Seric register ADDRESS *a; 12953149Seric { 12963149Seric static char buf[MAXNAME]; 12973149Seric struct mailer **mp; 12983149Seric register struct mailer *m; 12993149Seric 13003149Seric if (a == NULL) 13013149Seric a = (ADDRESS *) xalloc(sizeof *a); 130216889Seric bzero((char *) a, sizeof *a); 13033149Seric 13043149Seric /* figure out what net/mailer to use */ 130556326Seric if (*tv == NULL || **tv != CANONNET) 13064279Seric { 13073149Seric syserr("buildaddr: no net"); 13084279Seric return (NULL); 13094279Seric } 13103149Seric tv++; 131133725Sbostic if (!strcasecmp(*tv, "error")) 13124279Seric { 131310183Seric if (**++tv == CANONHOST) 131410183Seric { 131510183Seric setstat(atoi(*++tv)); 131610183Seric tv++; 131710183Seric } 131856326Seric buf[0] = '\0'; 131956326Seric for (; (*tv != NULL) && (**tv != CANONUSER); tv++) 132056326Seric { 132156326Seric if (buf[0] != '\0') 132256326Seric (void) strcat(buf, " "); 132356326Seric (void) strcat(buf, *tv); 132456326Seric } 132510183Seric if (**tv != CANONUSER) 13264279Seric syserr("buildaddr: error: no user"); 13274279Seric while (*++tv != NULL) 13284279Seric { 13294279Seric if (buf[0] != '\0') 13307005Seric (void) strcat(buf, " "); 13317005Seric (void) strcat(buf, *tv); 13324279Seric } 133356326Seric #ifdef LOG 133456326Seric if (LogLevel > 8) 133556326Seric syslog (LOG_DEBUG, "%s: Trace: $#ERROR $: %s", 133656326Seric CurEnv->e_id, buf); 133756326Seric #endif /* LOG */ 13384279Seric usrerr(buf); 13394279Seric return (NULL); 13404279Seric } 13414598Seric for (mp = Mailer; (m = *mp++) != NULL; ) 13423149Seric { 134333725Sbostic if (!strcasecmp(m->m_name, *tv)) 13443149Seric break; 13453149Seric } 13463149Seric if (m == NULL) 13474279Seric { 134824944Seric syserr("buildaddr: unknown mailer %s", *tv); 13494279Seric return (NULL); 13504279Seric } 13514598Seric a->q_mailer = m; 13523149Seric 13533149Seric /* figure out what host (if any) */ 135456326Seric if (**++tv != CANONHOST) 13553149Seric { 135656326Seric if (!bitnset(M_LOCAL, m->m_flags)) 13574279Seric { 13583149Seric syserr("buildaddr: no host"); 13594279Seric return (NULL); 13604279Seric } 136156326Seric else 136256326Seric a->q_host = NULL; 136356326Seric } 136456326Seric else 136556326Seric { 13665704Seric buf[0] = '\0'; 136756326Seric while (*++tv != NULL && **tv != CANONUSER) 136856326Seric (void) strcat(buf, *tv); 13695704Seric a->q_host = newstr(buf); 13703149Seric } 13713149Seric 13723149Seric /* figure out the user */ 137336615Sbostic if (*tv == NULL || **tv != CANONUSER) 13744279Seric { 13753149Seric syserr("buildaddr: no user"); 13764279Seric return (NULL); 13774279Seric } 137819040Seric 137956326Seric /* define tohost before running mailer rulesets */ 138056326Seric define('h', a->q_host, CurEnv); 138151317Seric 138219040Seric /* rewrite according recipient mailer rewriting rules */ 138319040Seric rewrite(++tv, 2); 1384*56327Seric if (m->m_r_rwset > 0) 1385*56327Seric rewrite(tv, m->m_r_rwset); 138619040Seric rewrite(tv, 4); 138719040Seric 138819040Seric /* save the result for the command line/RCPT argument */ 138911278Seric cataddr(tv, buf, sizeof buf); 13903149Seric a->q_user = buf; 13913149Seric 13923149Seric return (a); 13933149Seric } 13943188Seric /* 13954228Seric ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 13964228Seric ** 13974228Seric ** Parameters: 13984228Seric ** pvp -- parameter vector to rebuild. 13994228Seric ** buf -- buffer to build the string into. 14004228Seric ** sz -- size of buf. 14014228Seric ** 14024228Seric ** Returns: 14034228Seric ** none. 14044228Seric ** 14054228Seric ** Side Effects: 14064228Seric ** Destroys buf. 14074228Seric */ 14084228Seric 140956326Seric void 14104228Seric cataddr(pvp, buf, sz) 14114228Seric char **pvp; 14124228Seric char *buf; 14134228Seric register int sz; 14144228Seric { 14154228Seric bool oatomtok = FALSE; 141656326Seric bool natomtok; 14174228Seric register int i; 14184228Seric register char *p; 14194228Seric 14208423Seric if (pvp == NULL) 14218423Seric { 142223109Seric (void) strcpy(buf, ""); 14238423Seric return; 14248423Seric } 14254228Seric p = buf; 142611156Seric sz -= 2; 14274228Seric while (*pvp != NULL && (i = strlen(*pvp)) < sz) 14284228Seric { 14298078Seric natomtok = (toktype(**pvp) == ATM); 14304228Seric if (oatomtok && natomtok) 14319042Seric *p++ = SpaceSub; 14324228Seric (void) strcpy(p, *pvp); 14334228Seric oatomtok = natomtok; 14344228Seric p += i; 143511156Seric sz -= i + 1; 14364228Seric pvp++; 14374228Seric } 14384228Seric *p = '\0'; 14394228Seric } 14404228Seric /* 14413188Seric ** SAMEADDR -- Determine if two addresses are the same 14423188Seric ** 14433188Seric ** This is not just a straight comparison -- if the mailer doesn't 14443188Seric ** care about the host we just ignore it, etc. 14453188Seric ** 14463188Seric ** Parameters: 14473188Seric ** a, b -- pointers to the internal forms to compare. 14483188Seric ** 14493188Seric ** Returns: 14503188Seric ** TRUE -- they represent the same mailbox. 14513188Seric ** FALSE -- they don't. 14523188Seric ** 14533188Seric ** Side Effects: 14543188Seric ** none. 14553188Seric */ 14563188Seric 14573188Seric bool 14589374Seric sameaddr(a, b) 14593188Seric register ADDRESS *a; 14603188Seric register ADDRESS *b; 14613188Seric { 14623188Seric /* if they don't have the same mailer, forget it */ 14633188Seric if (a->q_mailer != b->q_mailer) 14643188Seric return (FALSE); 14653188Seric 14663188Seric /* if the user isn't the same, we can drop out */ 146756326Seric if (strcasecmp(a->q_user, b->q_user)) 14683188Seric return (FALSE); 14693188Seric 14703188Seric /* if the mailer ignores hosts, we have succeeded! */ 147110690Seric if (bitnset(M_LOCAL, a->q_mailer->m_flags)) 14723188Seric return (TRUE); 14733188Seric 14743188Seric /* otherwise compare hosts (but be careful for NULL ptrs) */ 14753188Seric if (a->q_host == NULL || b->q_host == NULL) 14763188Seric return (FALSE); 147756326Seric if (strcasecmp(a->q_host, b->q_host)) 14783188Seric return (FALSE); 14793188Seric 14803188Seric return (TRUE); 14813188Seric } 14823234Seric /* 14833234Seric ** PRINTADDR -- print address (for debugging) 14843234Seric ** 14853234Seric ** Parameters: 14863234Seric ** a -- the address to print 14873234Seric ** follow -- follow the q_next chain. 14883234Seric ** 14893234Seric ** Returns: 14903234Seric ** none. 14913234Seric ** 14923234Seric ** Side Effects: 14933234Seric ** none. 14943234Seric */ 14953234Seric 149656326Seric void 14973234Seric printaddr(a, follow) 14983234Seric register ADDRESS *a; 14993234Seric bool follow; 15003234Seric { 15015001Seric bool first = TRUE; 15025001Seric 15033234Seric while (a != NULL) 15043234Seric { 15055001Seric first = FALSE; 15064443Seric printf("%x=", a); 15074085Seric (void) fflush(stdout); 150840973Sbostic printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n", 150940973Sbostic a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name, 151040973Sbostic a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>"); 15118181Seric printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags, 15128181Seric a->q_alias); 15138181Seric printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home, 15148181Seric a->q_fullname); 15154996Seric 15163234Seric if (!follow) 15173234Seric return; 15184996Seric a = a->q_next; 15193234Seric } 15205001Seric if (first) 15214443Seric printf("[NULL]\n"); 15223234Seric } 15234317Seric 15247682Seric /* 15257682Seric ** REMOTENAME -- return the name relative to the current mailer 15267682Seric ** 15277682Seric ** Parameters: 15287682Seric ** name -- the name to translate. 15298069Seric ** m -- the mailer that we want to do rewriting relative 15308069Seric ** to. 15318069Seric ** senderaddress -- if set, uses the sender rewriting rules 15328069Seric ** rather than the recipient rewriting rules. 153310310Seric ** canonical -- if set, strip out any comment information, 153410310Seric ** etc. 15357682Seric ** 15367682Seric ** Returns: 15377682Seric ** the text string representing this address relative to 15387682Seric ** the receiving mailer. 15397682Seric ** 15407682Seric ** Side Effects: 15417682Seric ** none. 15427682Seric ** 15437682Seric ** Warnings: 15447682Seric ** The text string returned is tucked away locally; 15457682Seric ** copy it if you intend to save it. 15467682Seric */ 15477682Seric 15487682Seric char * 1549*56327Seric remotename(name, m, senderaddress, canonical) 15507682Seric char *name; 155156326Seric MAILER *m; 15528069Seric bool senderaddress; 155310310Seric bool canonical; 15547682Seric { 15558069Seric register char **pvp; 15568069Seric char *fancy; 155756326Seric char *oldg = macvalue('g', CurEnv); 15587682Seric static char buf[MAXNAME]; 15597682Seric char lbuf[MAXNAME]; 156016914Seric char pvpbuf[PSBUFSIZE]; 15617682Seric 15627755Seric if (tTd(12, 1)) 15637755Seric printf("remotename(%s)\n", name); 15647755Seric 156510177Seric /* don't do anything if we are tagging it as special */ 1566*56327Seric if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0) 156710177Seric return (name); 156810177Seric 15697682Seric /* 15708181Seric ** Do a heuristic crack of this name to extract any comment info. 15718181Seric ** This will leave the name as a comment and a $g macro. 15727889Seric */ 15737889Seric 157410310Seric if (canonical) 157516155Seric fancy = "\001g"; 157610310Seric else 157710310Seric fancy = crackaddr(name); 15787889Seric 15798181Seric /* 15808181Seric ** Turn the name into canonical form. 15818181Seric ** Normally this will be RFC 822 style, i.e., "user@domain". 15828181Seric ** If this only resolves to "user", and the "C" flag is 15838181Seric ** specified in the sending mailer, then the sender's 15848181Seric ** domain will be appended. 15858181Seric */ 15868181Seric 158716914Seric pvp = prescan(name, '\0', pvpbuf); 15887889Seric if (pvp == NULL) 15897889Seric return (name); 15908181Seric rewrite(pvp, 3); 159156326Seric if (CurEnv->e_fromdomain != NULL) 15928181Seric { 15938181Seric /* append from domain to this address */ 15948181Seric register char **pxp = pvp; 15958181Seric 15969594Seric /* see if there is an "@domain" in the current name */ 15978181Seric while (*pxp != NULL && strcmp(*pxp, "@") != 0) 15988181Seric pxp++; 15998181Seric if (*pxp == NULL) 16008181Seric { 16019594Seric /* no.... append the "@domain" from the sender */ 160256326Seric register char **qxq = CurEnv->e_fromdomain; 16038181Seric 16049594Seric while ((*pxp++ = *qxq++) != NULL) 16059594Seric continue; 160611726Seric rewrite(pvp, 3); 16078181Seric } 16088181Seric } 16098181Seric 16108181Seric /* 16118959Seric ** Do more specific rewriting. 161256326Seric ** Rewrite using ruleset 1 or 2 for envelope addresses and 161356326Seric ** 5 or 6 for header addresses depending on whether this 161456326Seric ** is a sender address or not. 16158181Seric ** Then run it through any receiving-mailer-specific rulesets. 16168181Seric */ 16178181Seric 16188069Seric if (senderaddress) 16197755Seric { 1620*56327Seric rewrite(pvp, 1); 1621*56327Seric if (m->m_s_rwset > 0) 1622*56327Seric rewrite(pvp, m->m_s_rwset); 16238069Seric } 16248069Seric else 16258069Seric { 1626*56327Seric rewrite(pvp, 2); 1627*56327Seric if (m->m_r_rwset > 0) 1628*56327Seric rewrite(pvp, m->m_r_rwset); 16297682Seric } 16307682Seric 16318181Seric /* 16328959Seric ** Do any final sanitation the address may require. 16338959Seric ** This will normally be used to turn internal forms 16348959Seric ** (e.g., user@host.LOCAL) into external form. This 16358959Seric ** may be used as a default to the above rules. 16368959Seric */ 16378959Seric 16388959Seric rewrite(pvp, 4); 16398959Seric 16408959Seric /* 16418181Seric ** Now restore the comment information we had at the beginning. 16428181Seric */ 16438181Seric 16447682Seric cataddr(pvp, lbuf, sizeof lbuf); 164556326Seric define('g', lbuf, CurEnv); 164656326Seric expand(fancy, buf, &buf[sizeof buf - 1], CurEnv); 164756326Seric define('g', oldg, CurEnv); 16487682Seric 16497682Seric if (tTd(12, 1)) 16507755Seric printf("remotename => `%s'\n", buf); 16517682Seric return (buf); 16527682Seric } 165351317Seric /* 165456326Seric ** UURELATIVIZE -- Make an address !-relative to recipient/sender nodes 165551317Seric ** 165651317Seric ** Parameters: 165756326Seric ** from -- the sending node (usually "$k" or "$w") 165856326Seric ** to -- the receiving node (usually "$h") 165956326Seric ** pvp -- address vector 166051317Seric ** 166151317Seric ** Returns: 166251317Seric ** none. 166356326Seric ** 166456326Seric ** Side Effects: 166556326Seric ** The pvp is rewritten to be relative the "to" node 166656326Seric ** wrt the "from" node. In other words, if the pvp 166756326Seric ** is headed by "to!" that part is stripped; otherwise 166856326Seric ** "from!" is prepended. Exception: "to!user" addresses 166956326Seric ** with no '!'s in the user part are sent as is. 167056326Seric ** 167156326Seric ** Bugs: 167256326Seric ** The pvp may overflow, but we don't catch it. 167351317Seric */ 167451317Seric 167556326Seric static void 167656326Seric uurelativize(from, to, pvp) 167756326Seric const char *from, *to; 167856326Seric char **pvp; 167951317Seric { 168056326Seric register char **pxp = pvp; 168156326Seric char expfrom[MAXNAME], expto[MAXNAME]; 168251317Seric 168356326Seric expand(from, expfrom, &expfrom[sizeof expfrom - 1], CurEnv); 168456326Seric expand(to, expto, &expto[sizeof expto - 1], CurEnv); 168551317Seric 168656326Seric /* 168756326Seric * supposing that we've got something, should 168856326Seric * we add "from!" or remove "to!"? 168956326Seric */ 169056326Seric if (pvp[0] != NULL) 169156326Seric if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 || 169256326Seric /*strcasecmp?*/ strcmp(pvp[0], expto) != 0) 169356326Seric { 169456326Seric /* either local name, no UUCP address, */ 169556326Seric /* or not to "to!" ==> prepend address with "from!" */ 169651317Seric 169756326Seric /* already there? */ 169856326Seric if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 || 169956326Seric /*strcasecmp?*/ strcmp(pvp[0], expfrom) != 0) 170056326Seric { 170151317Seric 170256326Seric /* no, put it there */ 170356326Seric while (*pxp != NULL) 170456326Seric pxp++; 170556326Seric do 170656326Seric pxp[2] = *pxp; 170756326Seric while (pxp-- != pvp); 170856326Seric pvp[0] = newstr(expfrom); 170956326Seric pvp[1] = "!"; 171056326Seric } 171156326Seric } 171256326Seric else 171356326Seric { 171456326Seric /* address is to "to!" -- remove if not "to!user" */ 171556326Seric for (pxp = &pvp[2]; 171656326Seric *pxp != NULL && strcmp(*pxp, "!") != 0; pxp++) 171756326Seric ; 171856326Seric if (*pxp != NULL) 171956326Seric for (pxp = pvp; *pxp != NULL; pxp++) 172056326Seric *pxp = pxp[2]; 172156326Seric } 172251317Seric } 1723