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