1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)parseaddr.c 6.9 (Berkeley) 01/20/93"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 15 /* 16 ** PARSEADDR -- Parse an address 17 ** 18 ** Parses an address and breaks it up into three parts: a 19 ** net to transmit the message on, the host to transmit it 20 ** to, and a user on that host. These are loaded into an 21 ** ADDRESS header with the values squirreled away if necessary. 22 ** The "user" part may not be a real user; the process may 23 ** just reoccur on that machine. For example, on a machine 24 ** with an arpanet connection, the address 25 ** csvax.bill@berkeley 26 ** will break up to a "user" of 'csvax.bill' and a host 27 ** of 'berkeley' -- to be transmitted over the arpanet. 28 ** 29 ** Parameters: 30 ** addr -- the address to parse. 31 ** a -- a pointer to the address descriptor buffer. 32 ** If NULL, a header will be created. 33 ** copyf -- determines what shall be copied: 34 ** -1 -- don't copy anything. The printname 35 ** (q_paddr) is just addr, and the 36 ** user & host are allocated internally 37 ** to parse. 38 ** 0 -- copy out the parsed user & host, but 39 ** don't copy the printname. 40 ** +1 -- copy everything. 41 ** delim -- the character to terminate the address, passed 42 ** to prescan. 43 ** e -- the envelope that will contain this address. 44 ** 45 ** Returns: 46 ** A pointer to the address descriptor header (`a' if 47 ** `a' is non-NULL). 48 ** NULL on error. 49 ** 50 ** Side Effects: 51 ** none 52 */ 53 54 /* following delimiters are inherent to the internal algorithms */ 55 # define DELIMCHARS "\001()<>,;\\\"\r\n" /* word delimiters */ 56 57 ADDRESS * 58 parseaddr(addr, a, copyf, delim, e) 59 char *addr; 60 register ADDRESS *a; 61 int copyf; 62 char delim; 63 register ENVELOPE *e; 64 { 65 register char **pvp; 66 char pvpbuf[PSBUFSIZE]; 67 extern char **prescan(); 68 extern ADDRESS *buildaddr(); 69 extern bool invalidaddr(); 70 71 /* 72 ** Initialize and prescan address. 73 */ 74 75 e->e_to = addr; 76 if (tTd(20, 1)) 77 printf("\n--parseaddr(%s)\n", addr); 78 79 if (invalidaddr(addr)) 80 { 81 if (tTd(20, 1)) 82 printf("parseaddr-->bad address\n"); 83 return NULL; 84 } 85 86 pvp = prescan(addr, delim, pvpbuf); 87 if (pvp == NULL) 88 { 89 if (tTd(20, 1)) 90 printf("parseaddr-->NULL\n"); 91 return (NULL); 92 } 93 94 /* 95 ** Apply rewriting rules. 96 ** Ruleset 0 does basic parsing. It must resolve. 97 */ 98 99 rewrite(pvp, 3); 100 rewrite(pvp, 0); 101 102 /* 103 ** See if we resolved to a real mailer. 104 */ 105 106 if (pvp[0][0] != CANONNET) 107 { 108 setstat(EX_USAGE); 109 usrerr("cannot resolve name"); 110 return (NULL); 111 } 112 113 /* 114 ** Build canonical address from pvp. 115 */ 116 117 a = buildaddr(pvp, a); 118 if (a == NULL) 119 return (NULL); 120 121 /* 122 ** Make local copies of the host & user and then 123 ** transport them out. 124 */ 125 126 allocaddr(a, copyf, addr); 127 128 /* 129 ** Compute return value. 130 */ 131 132 if (tTd(20, 1)) 133 { 134 printf("parseaddr-->"); 135 printaddr(a, FALSE); 136 } 137 138 return (a); 139 } 140 /* 141 ** INVALIDADDR -- check for address containing meta-characters 142 ** 143 ** Parameters: 144 ** addr -- the address to check. 145 ** 146 ** Returns: 147 ** TRUE -- if the address has any "wierd" characters 148 ** FALSE -- otherwise. 149 */ 150 151 bool 152 invalidaddr(addr) 153 register char *addr; 154 { 155 for (; *addr != '\0'; addr++) 156 { 157 if (!isascii((int) *addr & 0377) || 158 !iscntrl(*addr) || isspace(*addr)) 159 continue; 160 setstat(EX_USAGE); 161 usrerr("Address contained invalid control characters"); 162 return TRUE; 163 } 164 return FALSE; 165 } 166 /* 167 ** ALLOCADDR -- do local allocations of address on demand. 168 ** 169 ** Also lowercases the host name if requested. 170 ** 171 ** Parameters: 172 ** a -- the address to reallocate. 173 ** copyf -- the copy flag (see parseaddr for description). 174 ** paddr -- the printname of the address. 175 ** 176 ** Returns: 177 ** none. 178 ** 179 ** Side Effects: 180 ** Copies portions of a into local buffers as requested. 181 */ 182 183 allocaddr(a, copyf, paddr) 184 register ADDRESS *a; 185 int copyf; 186 char *paddr; 187 { 188 register MAILER *m = a->q_mailer; 189 190 if (copyf > 0 && paddr != NULL) 191 { 192 extern char *DelimChar; 193 char savec = *DelimChar; 194 195 *DelimChar = '\0'; 196 a->q_paddr = newstr(paddr); 197 *DelimChar = savec; 198 } 199 else 200 a->q_paddr = paddr; 201 202 if (a->q_user == NULL) 203 a->q_user = ""; 204 if (a->q_host == NULL) 205 a->q_host = ""; 206 207 if (copyf >= 0) 208 { 209 a->q_host = newstr(a->q_host); 210 if (a->q_user != a->q_paddr) 211 a->q_user = newstr(a->q_user); 212 } 213 214 if (a->q_paddr == NULL) 215 a->q_paddr = a->q_user; 216 217 /* 218 ** Convert host name to lower case if requested. 219 ** User name will be done later. 220 */ 221 222 if (!bitnset(M_HST_UPPER, m->m_flags)) 223 makelower(a->q_host); 224 } 225 /* 226 ** LOWERADDR -- map UPPER->lower case on addresses as requested. 227 ** 228 ** Parameters: 229 ** a -- address to be mapped. 230 ** 231 ** Returns: 232 ** none. 233 ** 234 ** Side Effects: 235 ** none. 236 */ 237 238 loweraddr(a) 239 register ADDRESS *a; 240 { 241 register MAILER *m = a->q_mailer; 242 243 if (!bitnset(M_USR_UPPER, m->m_flags)) 244 makelower(a->q_user); 245 } 246 /* 247 ** PRESCAN -- Prescan name and make it canonical 248 ** 249 ** Scans a name and turns it into a set of tokens. This process 250 ** deletes blanks and comments (in parentheses). 251 ** 252 ** This routine knows about quoted strings and angle brackets. 253 ** 254 ** There are certain subtleties to this routine. The one that 255 ** comes to mind now is that backslashes on the ends of names 256 ** are silently stripped off; this is intentional. The problem 257 ** is that some versions of sndmsg (like at LBL) set the kill 258 ** character to something other than @ when reading addresses; 259 ** so people type "csvax.eric\@berkeley" -- which screws up the 260 ** berknet mailer. 261 ** 262 ** Parameters: 263 ** addr -- the name to chomp. 264 ** delim -- the delimiter for the address, normally 265 ** '\0' or ','; \0 is accepted in any case. 266 ** If '\t' then we are reading the .cf file. 267 ** pvpbuf -- place to put the saved text -- note that 268 ** the pointers are static. 269 ** 270 ** Returns: 271 ** A pointer to a vector of tokens. 272 ** NULL on error. 273 ** 274 ** Side Effects: 275 ** sets DelimChar to point to the character matching 'delim'. 276 */ 277 278 /* states and character types */ 279 # define OPR 0 /* operator */ 280 # define ATM 1 /* atom */ 281 # define QST 2 /* in quoted string */ 282 # define SPC 3 /* chewing up spaces */ 283 # define ONE 4 /* pick up one character */ 284 285 # define NSTATES 5 /* number of states */ 286 # define TYPE 017 /* mask to select state type */ 287 288 /* meta bits for table */ 289 # define M 020 /* meta character; don't pass through */ 290 # define B 040 /* cause a break */ 291 # define MB M|B /* meta-break */ 292 293 static short StateTab[NSTATES][NSTATES] = 294 { 295 /* oldst chtype> OPR ATM QST SPC ONE */ 296 /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, 297 /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B, 298 /*QST*/ QST, QST, OPR, QST, QST, 299 /*SPC*/ OPR, ATM, QST, SPC|M, ONE, 300 /*ONE*/ OPR, OPR, OPR, OPR, OPR, 301 }; 302 303 # define NOCHAR -1 /* signal nothing in lookahead token */ 304 305 char *DelimChar; /* set to point to the delimiter */ 306 307 char ** 308 prescan(addr, delim, pvpbuf) 309 char *addr; 310 char delim; 311 char pvpbuf[]; 312 { 313 register char *p; 314 register char *q; 315 register int c; 316 char **avp; 317 bool bslashmode; 318 int cmntcnt; 319 int anglecnt; 320 char *tok; 321 int state; 322 int newstate; 323 static char *av[MAXATOM+1]; 324 extern int errno; 325 326 /* make sure error messages don't have garbage on them */ 327 errno = 0; 328 329 q = pvpbuf; 330 bslashmode = FALSE; 331 cmntcnt = 0; 332 anglecnt = 0; 333 avp = av; 334 state = ATM; 335 c = NOCHAR; 336 p = addr; 337 if (tTd(22, 11)) 338 { 339 printf("prescan: "); 340 xputs(p); 341 (void) putchar('\n'); 342 } 343 344 do 345 { 346 /* read a token */ 347 tok = q; 348 for (;;) 349 { 350 /* store away any old lookahead character */ 351 if (c != NOCHAR) 352 { 353 /* see if there is room */ 354 if (q >= &pvpbuf[PSBUFSIZE - 5]) 355 { 356 usrerr("Address too long"); 357 DelimChar = p; 358 return (NULL); 359 } 360 361 /* squirrel it away */ 362 *q++ = c; 363 } 364 365 /* read a new input character */ 366 c = *p++; 367 if (c == '\0') 368 { 369 /* diagnose and patch up bad syntax */ 370 if (state == QST) 371 { 372 usrerr("Unbalanced '\"'"); 373 c = '"'; 374 } 375 else if (cmntcnt > 0) 376 { 377 usrerr("Unbalanced '('"); 378 c = ')'; 379 } 380 else if (anglecnt > 0) 381 { 382 c = '>'; 383 usrerr("Unbalanced '<'"); 384 } 385 else 386 break; 387 388 p--; 389 } 390 else if (c == delim && anglecnt <= 0 && 391 cmntcnt <= 0 && state != QST) 392 break; 393 394 if (tTd(22, 101)) 395 printf("c=%c, s=%d; ", c, state); 396 397 /* chew up special characters */ 398 *q = '\0'; 399 if (bslashmode) 400 { 401 /* kludge \! for naive users */ 402 if (c != '!') 403 *q++ = '\\'; 404 bslashmode = FALSE; 405 continue; 406 } 407 408 if (c == '\\') 409 { 410 bslashmode = TRUE; 411 c = NOCHAR; 412 continue; 413 } 414 else if (state == QST) 415 { 416 /* do nothing, just avoid next clauses */ 417 } 418 else if (c == '(') 419 { 420 cmntcnt++; 421 c = NOCHAR; 422 } 423 else if (c == ')') 424 { 425 if (cmntcnt <= 0) 426 { 427 usrerr("Unbalanced ')'"); 428 DelimChar = p; 429 return (NULL); 430 } 431 else 432 cmntcnt--; 433 } 434 else if (cmntcnt > 0) 435 c = NOCHAR; 436 else if (c == '<') 437 anglecnt++; 438 else if (c == '>') 439 { 440 if (anglecnt <= 0) 441 { 442 usrerr("Unbalanced '>'"); 443 DelimChar = p; 444 return (NULL); 445 } 446 anglecnt--; 447 } 448 else if (delim == ' ' && isspace(c)) 449 c = ' '; 450 451 if (c == NOCHAR) 452 continue; 453 454 /* see if this is end of input */ 455 if (c == delim && anglecnt <= 0 && state != QST) 456 break; 457 458 newstate = StateTab[state][toktype(c)]; 459 if (tTd(22, 101)) 460 printf("ns=%02o\n", newstate); 461 state = newstate & TYPE; 462 if (bitset(M, newstate)) 463 c = NOCHAR; 464 if (bitset(B, newstate)) 465 break; 466 } 467 468 /* new token */ 469 if (tok != q) 470 { 471 *q++ = '\0'; 472 if (tTd(22, 36)) 473 { 474 printf("tok="); 475 xputs(tok); 476 (void) putchar('\n'); 477 } 478 if (avp >= &av[MAXATOM]) 479 { 480 syserr("prescan: too many tokens"); 481 DelimChar = p; 482 return (NULL); 483 } 484 *avp++ = tok; 485 } 486 } while (c != '\0' && (c != delim || anglecnt > 0)); 487 *avp = NULL; 488 DelimChar = --p; 489 if (tTd(22, 12)) 490 { 491 printf("prescan==>"); 492 printav(av); 493 } 494 if (av[0] != NULL) 495 return (av); 496 return (NULL); 497 } 498 /* 499 ** TOKTYPE -- return token type 500 ** 501 ** Parameters: 502 ** c -- the character in question. 503 ** 504 ** Returns: 505 ** Its type. 506 ** 507 ** Side Effects: 508 ** none. 509 */ 510 511 toktype(c) 512 register char c; 513 { 514 static char buf[50]; 515 static bool firstime = TRUE; 516 517 if (firstime) 518 { 519 firstime = FALSE; 520 expand("\001o", buf, &buf[sizeof buf - 1], CurEnv); 521 (void) strcat(buf, DELIMCHARS); 522 } 523 if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS) 524 return (ONE); 525 if (c == '"') 526 return (QST); 527 if (!isascii(c)) 528 return (ATM); 529 if (isspace(c) || c == ')') 530 return (SPC); 531 if (iscntrl(c) || strchr(buf, c) != NULL) 532 return (OPR); 533 return (ATM); 534 } 535 /* 536 ** REWRITE -- apply rewrite rules to token vector. 537 ** 538 ** This routine is an ordered production system. Each rewrite 539 ** rule has a LHS (called the pattern) and a RHS (called the 540 ** rewrite); 'rwr' points the the current rewrite rule. 541 ** 542 ** For each rewrite rule, 'avp' points the address vector we 543 ** are trying to match against, and 'pvp' points to the pattern. 544 ** If pvp points to a special match value (MATCHZANY, MATCHANY, 545 ** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp 546 ** matched is saved away in the match vector (pointed to by 'mvp'). 547 ** 548 ** When a match between avp & pvp does not match, we try to 549 ** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS 550 ** we must also back out the match in mvp. If we reach a 551 ** MATCHANY or MATCHZANY we just extend the match and start 552 ** over again. 553 ** 554 ** When we finally match, we rewrite the address vector 555 ** and try over again. 556 ** 557 ** Parameters: 558 ** pvp -- pointer to token vector. 559 ** 560 ** Returns: 561 ** none. 562 ** 563 ** Side Effects: 564 ** pvp is modified. 565 */ 566 567 struct match 568 { 569 char **first; /* first token matched */ 570 char **last; /* last token matched */ 571 }; 572 573 # define MAXMATCH 9 /* max params per rewrite */ 574 575 576 rewrite(pvp, ruleset) 577 char **pvp; 578 int ruleset; 579 { 580 register char *ap; /* address pointer */ 581 register char *rp; /* rewrite pointer */ 582 register char **avp; /* address vector pointer */ 583 register char **rvp; /* rewrite vector pointer */ 584 register struct match *mlp; /* cur ptr into mlist */ 585 register struct rewrite *rwr; /* pointer to current rewrite rule */ 586 struct match mlist[MAXMATCH]; /* stores match on LHS */ 587 char *npvp[MAXATOM+1]; /* temporary space for rebuild */ 588 589 if (OpMode == MD_TEST || tTd(21, 2)) 590 { 591 printf("rewrite: ruleset %2d input:", ruleset); 592 printav(pvp); 593 } 594 if (ruleset < 0 || ruleset >= MAXRWSETS) 595 { 596 syserr("rewrite: illegal ruleset number %d", ruleset); 597 return; 598 } 599 if (pvp == NULL) 600 return; 601 602 /* 603 ** Run through the list of rewrite rules, applying 604 ** any that match. 605 */ 606 607 for (rwr = RewriteRules[ruleset]; rwr != NULL; ) 608 { 609 int loopcount = 0; 610 611 if (tTd(21, 12)) 612 { 613 printf("-----trying rule:"); 614 printav(rwr->r_lhs); 615 } 616 617 /* try to match on this rule */ 618 mlp = mlist; 619 rvp = rwr->r_lhs; 620 avp = pvp; 621 while ((ap = *avp) != NULL || *rvp != NULL) 622 { 623 if (++loopcount > 100) 624 { 625 syserr("Infinite loop in ruleset %d", ruleset); 626 printf("workspace: "); 627 printav(pvp); 628 break; 629 } 630 rp = *rvp; 631 if (tTd(21, 35)) 632 { 633 printf("rp="); 634 xputs(rp); 635 printf(", ap="); 636 xputs(ap); 637 printf("\n"); 638 } 639 if (rp == NULL) 640 { 641 /* end-of-pattern before end-of-address */ 642 goto backup; 643 } 644 if (ap == NULL && *rp != MATCHZANY) 645 { 646 /* end-of-input */ 647 break; 648 } 649 650 switch (*rp) 651 { 652 register STAB *s; 653 654 case MATCHCLASS: 655 case MATCHNCLASS: 656 /* match any token in (not in) a class */ 657 s = stab(ap, ST_CLASS, ST_FIND); 658 if (s == NULL || !bitnset(rp[1], s->s_class)) 659 { 660 if (*rp == MATCHCLASS) 661 goto backup; 662 } 663 else if (*rp == MATCHNCLASS) 664 goto backup; 665 666 /* explicit fall-through */ 667 668 case MATCHONE: 669 case MATCHANY: 670 /* match exactly one token */ 671 mlp->first = avp; 672 mlp->last = avp++; 673 mlp++; 674 break; 675 676 case MATCHZANY: 677 /* match zero or more tokens */ 678 mlp->first = avp; 679 mlp->last = avp - 1; 680 mlp++; 681 break; 682 683 default: 684 /* must have exact match */ 685 if (strcasecmp(rp, ap)) 686 goto backup; 687 avp++; 688 break; 689 } 690 691 /* successful match on this token */ 692 rvp++; 693 continue; 694 695 backup: 696 /* match failed -- back up */ 697 while (--rvp >= rwr->r_lhs) 698 { 699 rp = *rvp; 700 if (*rp == MATCHANY || *rp == MATCHZANY) 701 { 702 /* extend binding and continue */ 703 avp = ++mlp[-1].last; 704 avp++; 705 rvp++; 706 break; 707 } 708 avp--; 709 if (*rp == MATCHONE || *rp == MATCHCLASS || 710 *rp == MATCHNCLASS) 711 { 712 /* back out binding */ 713 mlp--; 714 } 715 } 716 717 if (rvp < rwr->r_lhs) 718 { 719 /* total failure to match */ 720 break; 721 } 722 } 723 724 /* 725 ** See if we successfully matched 726 */ 727 728 if (rvp < rwr->r_lhs || *rvp != NULL) 729 { 730 if (tTd(21, 10)) 731 printf("----- rule fails\n"); 732 rwr = rwr->r_next; 733 continue; 734 } 735 736 rvp = rwr->r_rhs; 737 if (tTd(21, 12)) 738 { 739 printf("-----rule matches:"); 740 printav(rvp); 741 } 742 743 rp = *rvp; 744 if (*rp == CANONUSER) 745 { 746 rvp++; 747 rwr = rwr->r_next; 748 } 749 else if (*rp == CANONHOST) 750 { 751 rvp++; 752 rwr = NULL; 753 } 754 else if (*rp == CANONNET) 755 rwr = NULL; 756 757 /* substitute */ 758 for (avp = npvp; *rvp != NULL; rvp++) 759 { 760 register struct match *m; 761 register char **pp; 762 763 rp = *rvp; 764 if (*rp == MATCHREPL) 765 { 766 /* substitute from LHS */ 767 m = &mlist[rp[1] - '1']; 768 if (m < mlist || m >= mlp) 769 { 770 syserr("rewrite: ruleset %d: replacement $%c out of bounds", 771 ruleset, rp[1]); 772 return; 773 } 774 if (tTd(21, 15)) 775 { 776 printf("$%c:", rp[1]); 777 pp = m->first; 778 while (pp <= m->last) 779 { 780 printf(" %x=\"", *pp); 781 (void) fflush(stdout); 782 printf("%s\"", *pp++); 783 } 784 printf("\n"); 785 } 786 pp = m->first; 787 while (pp <= m->last) 788 { 789 if (avp >= &npvp[MAXATOM]) 790 { 791 syserr("rewrite: expansion too long"); 792 return; 793 } 794 *avp++ = *pp++; 795 } 796 } 797 else 798 { 799 /* vanilla replacement */ 800 if (avp >= &npvp[MAXATOM]) 801 { 802 toolong: 803 syserr("rewrite: expansion too long"); 804 return; 805 } 806 *avp++ = rp; 807 } 808 } 809 *avp++ = NULL; 810 811 /* 812 ** Check for any hostname/keyword lookups. 813 */ 814 815 for (rvp = npvp; *rvp != NULL; rvp++) 816 { 817 char **hbrvp; 818 char **xpvp; 819 int trsize; 820 char *olddelimchar; 821 char *replac; 822 int endtoken; 823 STAB *map; 824 char *mapname; 825 char **key_rvp; 826 char **arg_rvp; 827 char **default_rvp; 828 char buf[MAXNAME + 1]; 829 char *pvpb1[MAXATOM + 1]; 830 char *argvect[10]; 831 char pvpbuf[PSBUFSIZE]; 832 extern char *DelimChar; 833 834 if (**rvp != HOSTBEGIN && **rvp != LOOKUPBEGIN) 835 continue; 836 837 /* 838 ** Got a hostname/keyword lookup. 839 ** 840 ** This could be optimized fairly easily. 841 */ 842 843 hbrvp = rvp; 844 if (**rvp == HOSTBEGIN) 845 { 846 endtoken = HOSTEND; 847 mapname = "host"; 848 } 849 else 850 { 851 endtoken = LOOKUPEND; 852 mapname = *++rvp; 853 } 854 map = stab(mapname, ST_MAP, ST_FIND); 855 if (map == NULL) 856 syserr("rewrite: map %s not found", mapname); 857 858 /* extract the match part */ 859 key_rvp = ++rvp; 860 default_rvp = NULL; 861 arg_rvp = argvect; 862 xpvp = NULL; 863 replac = pvpbuf; 864 while (*rvp != NULL && **rvp != endtoken) 865 { 866 int nodetype = **rvp; 867 868 if (nodetype != CANONHOST && nodetype != CANONUSER) 869 { 870 rvp++; 871 continue; 872 } 873 874 *rvp++ = NULL; 875 876 if (xpvp != NULL) 877 { 878 cataddr(xpvp, replac, 879 &pvpbuf[sizeof pvpbuf] - replac); 880 *++arg_rvp = replac; 881 replac += strlen(replac) + 1; 882 xpvp = NULL; 883 } 884 switch (nodetype) 885 { 886 case CANONHOST: 887 xpvp = rvp; 888 break; 889 890 case CANONUSER: 891 default_rvp = rvp; 892 break; 893 } 894 } 895 if (*rvp != NULL) 896 *rvp++ = NULL; 897 if (xpvp != NULL) 898 { 899 cataddr(xpvp, replac, 900 &pvpbuf[sizeof pvpbuf] - replac); 901 *++arg_rvp = replac; 902 } 903 *++arg_rvp = NULL; 904 905 /* save the remainder of the input string */ 906 trsize = (int) (avp - rvp + 1) * sizeof *rvp; 907 bcopy((char *) rvp, (char *) pvpb1, trsize); 908 909 /* look it up */ 910 cataddr(key_rvp, buf, sizeof buf); 911 argvect[0] = buf; 912 if (map != NULL && bitset(MF_VALID, map->s_map.map_flags)) 913 { 914 int bsize = sizeof buf - 1; 915 916 if (map->s_map.map_app != NULL) 917 bsize -= strlen(map->s_map.map_app); 918 replac = (*map->s_map.map_class->map_lookup)(&map->s_map, 919 buf, sizeof buf - 1, argvect); 920 if (replac != NULL && map->s_map.map_app != NULL) 921 strcat(replac, map->s_map.map_app); 922 } 923 else 924 replac = NULL; 925 926 /* if no replacement, use default */ 927 if (replac == NULL && default_rvp != NULL) 928 { 929 char buf2[sizeof buf]; 930 931 /* rewrite the default with % translations */ 932 cataddr(default_rvp, buf2, sizeof buf2); 933 map_rewrite(buf2, sizeof buf2, buf, sizeof buf, 934 argvect); 935 replac = buf; 936 } 937 938 if (replac == NULL) 939 { 940 xpvp = key_rvp; 941 } 942 else 943 { 944 /* scan the new replacement */ 945 olddelimchar = DelimChar; 946 xpvp = prescan(replac, '\0', pvpbuf); 947 DelimChar = olddelimchar; 948 if (xpvp == NULL) 949 { 950 syserr("rewrite: cannot prescan map value: %s", replac); 951 return; 952 } 953 } 954 955 /* append it to the token list */ 956 for (avp = hbrvp; *xpvp != NULL; xpvp++) 957 { 958 *avp++ = newstr(*xpvp); 959 if (avp >= &npvp[MAXATOM]) 960 goto toolong; 961 } 962 963 /* restore the old trailing information */ 964 for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) 965 if (avp >= &npvp[MAXATOM]) 966 goto toolong; 967 968 break; 969 } 970 971 /* 972 ** Check for subroutine calls. 973 */ 974 975 if (*npvp != NULL && **npvp == CALLSUBR) 976 { 977 bcopy((char *) &npvp[2], (char *) pvp, 978 (int) (avp - npvp - 2) * sizeof *avp); 979 if (tTd(21, 3)) 980 printf("-----callsubr %s\n", npvp[1]); 981 rewrite(pvp, atoi(npvp[1])); 982 } 983 else 984 { 985 bcopy((char *) npvp, (char *) pvp, 986 (int) (avp - npvp) * sizeof *avp); 987 } 988 if (tTd(21, 4)) 989 { 990 printf("rewritten as:"); 991 printav(pvp); 992 } 993 } 994 995 if (OpMode == MD_TEST || tTd(21, 2)) 996 { 997 printf("rewrite: ruleset %2d returns:", ruleset); 998 printav(pvp); 999 } 1000 } 1001 /* 1002 ** BUILDADDR -- build address from token vector. 1003 ** 1004 ** Parameters: 1005 ** tv -- token vector. 1006 ** a -- pointer to address descriptor to fill. 1007 ** If NULL, one will be allocated. 1008 ** 1009 ** Returns: 1010 ** NULL if there was an error. 1011 ** 'a' otherwise. 1012 ** 1013 ** Side Effects: 1014 ** fills in 'a' 1015 */ 1016 1017 struct errcodes 1018 { 1019 char *ec_name; /* name of error code */ 1020 int ec_code; /* numeric code */ 1021 } ErrorCodes[] = 1022 { 1023 "usage", EX_USAGE, 1024 "nouser", EX_NOUSER, 1025 "nohost", EX_NOHOST, 1026 "unavailable", EX_UNAVAILABLE, 1027 "software", EX_SOFTWARE, 1028 "tempfail", EX_TEMPFAIL, 1029 "protocol", EX_PROTOCOL, 1030 #ifdef EX_CONFIG 1031 "config", EX_CONFIG, 1032 #endif 1033 NULL, EX_UNAVAILABLE, 1034 }; 1035 1036 ADDRESS * 1037 buildaddr(tv, a) 1038 register char **tv; 1039 register ADDRESS *a; 1040 { 1041 struct mailer **mp; 1042 register struct mailer *m; 1043 static char buf[MAXNAME]; 1044 1045 if (a == NULL) 1046 a = (ADDRESS *) xalloc(sizeof *a); 1047 bzero((char *) a, sizeof *a); 1048 1049 /* figure out what net/mailer to use */ 1050 if (**tv != CANONNET) 1051 { 1052 syserr("buildaddr: no net"); 1053 return (NULL); 1054 } 1055 tv++; 1056 if (!strcasecmp(*tv, "error")) 1057 { 1058 if (**++tv == CANONHOST) 1059 { 1060 register struct errcodes *ep; 1061 1062 if (isdigit(**++tv)) 1063 { 1064 setstat(atoi(*tv)); 1065 } 1066 else 1067 { 1068 for (ep = ErrorCodes; ep->ec_name != NULL; ep++) 1069 if (strcasecmp(ep->ec_name, *tv) == 0) 1070 break; 1071 setstat(ep->ec_code); 1072 } 1073 tv++; 1074 } 1075 if (**tv != CANONUSER) 1076 syserr("buildaddr: error: no user"); 1077 buf[0] = '\0'; 1078 while (*++tv != NULL) 1079 { 1080 if (buf[0] != '\0') 1081 (void) strcat(buf, " "); 1082 (void) strcat(buf, *tv); 1083 } 1084 usrerr(buf); 1085 return (NULL); 1086 } 1087 1088 for (mp = Mailer; (m = *mp++) != NULL; ) 1089 { 1090 if (!strcasecmp(m->m_name, *tv)) 1091 break; 1092 } 1093 if (m == NULL) 1094 { 1095 syserr("buildaddr: unknown mailer %s", *tv); 1096 return (NULL); 1097 } 1098 a->q_mailer = m; 1099 1100 /* figure out what host (if any) */ 1101 tv++; 1102 if (!bitnset(M_LOCAL, m->m_flags)) 1103 { 1104 if (**tv++ != CANONHOST) 1105 { 1106 syserr("buildaddr: no host"); 1107 return (NULL); 1108 } 1109 buf[0] = '\0'; 1110 while (*tv != NULL && **tv != CANONUSER) 1111 (void) strcat(buf, *tv++); 1112 a->q_host = newstr(buf); 1113 } 1114 else 1115 a->q_host = NULL; 1116 1117 /* figure out the user */ 1118 if (*tv == NULL || **tv != CANONUSER) 1119 { 1120 syserr("buildaddr: no user"); 1121 return (NULL); 1122 } 1123 1124 if (m == LocalMailer && tv[1] != NULL && strcmp(tv[1], "@") == 0) 1125 { 1126 tv++; 1127 a->q_flags |= QNOTREMOTE; 1128 } 1129 tv++; 1130 1131 /* do special mapping for local mailer */ 1132 if (m == LocalMailer && *tv != NULL) 1133 { 1134 register char *p = *tv; 1135 1136 if (*p == '"') 1137 p++; 1138 if (*p == '|') 1139 a->q_mailer = m = ProgMailer; 1140 else if (*p == '/') 1141 a->q_mailer = m = FileMailer; 1142 else if (*p == ':') 1143 { 1144 /* may be :include: */ 1145 cataddr(tv, buf, sizeof buf); 1146 p = buf; 1147 if (*p == '"') 1148 p++; 1149 p[9] = '\0'; 1150 if (strcasecmp(p, ":include:") == 0) 1151 a->q_mailer = m = InclMailer; 1152 } 1153 } 1154 1155 /* rewrite according recipient mailer rewriting rules */ 1156 rewrite(tv, 2); 1157 if (m->m_r_rwset > 0) 1158 rewrite(tv, m->m_r_rwset); 1159 rewrite(tv, 4); 1160 1161 /* save the result for the command line/RCPT argument */ 1162 cataddr(tv, buf, sizeof buf); 1163 a->q_user = buf; 1164 1165 return (a); 1166 } 1167 /* 1168 ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 1169 ** 1170 ** Parameters: 1171 ** pvp -- parameter vector to rebuild. 1172 ** buf -- buffer to build the string into. 1173 ** sz -- size of buf. 1174 ** 1175 ** Returns: 1176 ** none. 1177 ** 1178 ** Side Effects: 1179 ** Destroys buf. 1180 */ 1181 1182 cataddr(pvp, buf, sz) 1183 char **pvp; 1184 char *buf; 1185 register int sz; 1186 { 1187 bool oatomtok = FALSE; 1188 bool natomtok = FALSE; 1189 register int i; 1190 register char *p; 1191 1192 if (pvp == NULL) 1193 { 1194 (void) strcpy(buf, ""); 1195 return; 1196 } 1197 p = buf; 1198 sz -= 2; 1199 while (*pvp != NULL && (i = strlen(*pvp)) < sz) 1200 { 1201 natomtok = (toktype(**pvp) == ATM); 1202 if (oatomtok && natomtok) 1203 *p++ = SpaceSub; 1204 (void) strcpy(p, *pvp); 1205 oatomtok = natomtok; 1206 p += i; 1207 sz -= i + 1; 1208 pvp++; 1209 } 1210 *p = '\0'; 1211 } 1212 /* 1213 ** SAMEADDR -- Determine if two addresses are the same 1214 ** 1215 ** This is not just a straight comparison -- if the mailer doesn't 1216 ** care about the host we just ignore it, etc. 1217 ** 1218 ** Parameters: 1219 ** a, b -- pointers to the internal forms to compare. 1220 ** 1221 ** Returns: 1222 ** TRUE -- they represent the same mailbox. 1223 ** FALSE -- they don't. 1224 ** 1225 ** Side Effects: 1226 ** none. 1227 */ 1228 1229 bool 1230 sameaddr(a, b) 1231 register ADDRESS *a; 1232 register ADDRESS *b; 1233 { 1234 /* if they don't have the same mailer, forget it */ 1235 if (a->q_mailer != b->q_mailer) 1236 return (FALSE); 1237 1238 /* if the user isn't the same, we can drop out */ 1239 if (strcmp(a->q_user, b->q_user) != 0) 1240 return (FALSE); 1241 1242 /* if the mailer ignores hosts, we have succeeded! */ 1243 if (bitnset(M_LOCAL, a->q_mailer->m_flags)) 1244 return (TRUE); 1245 1246 /* otherwise compare hosts (but be careful for NULL ptrs) */ 1247 if (a->q_host == NULL || b->q_host == NULL) 1248 return (FALSE); 1249 if (strcmp(a->q_host, b->q_host) != 0) 1250 return (FALSE); 1251 1252 return (TRUE); 1253 } 1254 /* 1255 ** PRINTADDR -- print address (for debugging) 1256 ** 1257 ** Parameters: 1258 ** a -- the address to print 1259 ** follow -- follow the q_next chain. 1260 ** 1261 ** Returns: 1262 ** none. 1263 ** 1264 ** Side Effects: 1265 ** none. 1266 */ 1267 1268 printaddr(a, follow) 1269 register ADDRESS *a; 1270 bool follow; 1271 { 1272 bool first = TRUE; 1273 1274 while (a != NULL) 1275 { 1276 first = FALSE; 1277 printf("%x=", a); 1278 (void) fflush(stdout); 1279 printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n", 1280 a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name, 1281 a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>"); 1282 printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags, 1283 a->q_alias); 1284 printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home, 1285 a->q_fullname); 1286 1287 if (!follow) 1288 return; 1289 a = a->q_next; 1290 } 1291 if (first) 1292 printf("[NULL]\n"); 1293 } 1294 1295 /* 1296 ** REMOTENAME -- return the name relative to the current mailer 1297 ** 1298 ** Parameters: 1299 ** name -- the name to translate. 1300 ** m -- the mailer that we want to do rewriting relative 1301 ** to. 1302 ** senderaddress -- if set, uses the sender rewriting rules 1303 ** rather than the recipient rewriting rules. 1304 ** canonical -- if set, strip out any comment information, 1305 ** etc. 1306 ** 1307 ** Returns: 1308 ** the text string representing this address relative to 1309 ** the receiving mailer. 1310 ** 1311 ** Side Effects: 1312 ** none. 1313 ** 1314 ** Warnings: 1315 ** The text string returned is tucked away locally; 1316 ** copy it if you intend to save it. 1317 */ 1318 1319 char * 1320 remotename(name, m, senderaddress, canonical, e) 1321 char *name; 1322 struct mailer *m; 1323 bool senderaddress; 1324 bool canonical; 1325 register ENVELOPE *e; 1326 { 1327 register char **pvp; 1328 char *fancy; 1329 extern char *macvalue(); 1330 char *oldg = macvalue('g', e); 1331 static char buf[MAXNAME]; 1332 char lbuf[MAXNAME]; 1333 char pvpbuf[PSBUFSIZE]; 1334 extern char **prescan(); 1335 extern char *crackaddr(); 1336 1337 if (tTd(12, 1)) 1338 printf("remotename(%s)\n", name); 1339 1340 /* don't do anything if we are tagging it as special */ 1341 if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0) 1342 return (name); 1343 1344 /* 1345 ** Do a heuristic crack of this name to extract any comment info. 1346 ** This will leave the name as a comment and a $g macro. 1347 */ 1348 1349 if (canonical) 1350 fancy = "\001g"; 1351 else 1352 fancy = crackaddr(name); 1353 1354 /* 1355 ** Turn the name into canonical form. 1356 ** Normally this will be RFC 822 style, i.e., "user@domain". 1357 ** If this only resolves to "user", and the "C" flag is 1358 ** specified in the sending mailer, then the sender's 1359 ** domain will be appended. 1360 */ 1361 1362 pvp = prescan(name, '\0', pvpbuf); 1363 if (pvp == NULL) 1364 return (name); 1365 rewrite(pvp, 3); 1366 if (e->e_fromdomain != NULL) 1367 { 1368 /* append from domain to this address */ 1369 register char **pxp = pvp; 1370 1371 /* see if there is an "@domain" in the current name */ 1372 while (*pxp != NULL && strcmp(*pxp, "@") != 0) 1373 pxp++; 1374 if (*pxp == NULL) 1375 { 1376 /* no.... append the "@domain" from the sender */ 1377 register char **qxq = e->e_fromdomain; 1378 1379 while ((*pxp++ = *qxq++) != NULL) 1380 continue; 1381 rewrite(pvp, 3); 1382 } 1383 } 1384 1385 /* 1386 ** Do more specific rewriting. 1387 ** Rewrite using ruleset 1 or 2 depending on whether this is 1388 ** a sender address or not. 1389 ** Then run it through any receiving-mailer-specific rulesets. 1390 */ 1391 1392 if (senderaddress) 1393 { 1394 rewrite(pvp, 1); 1395 if (m->m_s_rwset > 0) 1396 rewrite(pvp, m->m_s_rwset); 1397 } 1398 else 1399 { 1400 rewrite(pvp, 2); 1401 if (m->m_r_rwset > 0) 1402 rewrite(pvp, m->m_r_rwset); 1403 } 1404 1405 /* 1406 ** Do any final sanitation the address may require. 1407 ** This will normally be used to turn internal forms 1408 ** (e.g., user@host.LOCAL) into external form. This 1409 ** may be used as a default to the above rules. 1410 */ 1411 1412 rewrite(pvp, 4); 1413 1414 /* 1415 ** Now restore the comment information we had at the beginning. 1416 */ 1417 1418 cataddr(pvp, lbuf, sizeof lbuf); 1419 define('g', lbuf, e); 1420 expand(fancy, buf, &buf[sizeof buf - 1], e); 1421 define('g', oldg, e); 1422 1423 if (tTd(12, 1)) 1424 printf("remotename => `%s'\n", buf); 1425 return (buf); 1426 } 1427 /* 1428 ** MAPLOCALUSER -- run local username through ruleset 5 for final redirection 1429 ** 1430 ** Parameters: 1431 ** a -- the address to map (but just the user name part). 1432 ** sendq -- the sendq in which to install any replacement 1433 ** addresses. 1434 ** 1435 ** Returns: 1436 ** none. 1437 */ 1438 1439 maplocaluser(a, sendq, e) 1440 register ADDRESS *a; 1441 ADDRESS **sendq; 1442 ENVELOPE *e; 1443 { 1444 register char **pvp; 1445 register ADDRESS *a1 = NULL; 1446 char pvpbuf[PSBUFSIZE]; 1447 1448 if (tTd(29, 1)) 1449 { 1450 printf("maplocaluser: "); 1451 printaddr(a, FALSE); 1452 } 1453 pvp = prescan(a->q_user, '\0', pvpbuf); 1454 if (pvp == NULL) 1455 return; 1456 1457 rewrite(pvp, 5); 1458 if (pvp[0] == NULL || pvp[0][0] != CANONNET) 1459 return; 1460 1461 /* if non-null, mailer destination specified -- has it changed? */ 1462 a1 = buildaddr(pvp, NULL); 1463 if (a1 == NULL || sameaddr(a, a1)) 1464 return; 1465 1466 /* mark old address as dead; insert new address */ 1467 a->q_flags |= QDONTSEND; 1468 a1->q_alias = a; 1469 allocaddr(a1, 1, NULL); 1470 (void) recipient(a1, sendq, e); 1471 } 1472