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.6 (Berkeley) 01/13/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 (c == delim && anglecnt <= 0 && cmntcnt <= 0)) 369 { 370 /* diagnose and patch up bad syntax */ 371 if (state == QST) 372 { 373 usrerr("Unbalanced '\"'"); 374 c = '"'; 375 } 376 else if (cmntcnt > 0) 377 { 378 usrerr("Unbalanced '('"); 379 c = ')'; 380 } 381 else if (anglecnt > 0) 382 { 383 c = '>'; 384 usrerr("Unbalanced '<'"); 385 } 386 else 387 break; 388 389 p--; 390 } 391 392 if (tTd(22, 101)) 393 printf("c=%c, s=%d; ", c, state); 394 395 /* chew up special characters */ 396 *q = '\0'; 397 if (bslashmode) 398 { 399 /* kludge \! for naive users */ 400 if (c != '!') 401 *q++ = '\\'; 402 bslashmode = FALSE; 403 continue; 404 } 405 406 if (c == '\\') 407 { 408 bslashmode = TRUE; 409 c = NOCHAR; 410 continue; 411 } 412 else if (state == QST) 413 { 414 /* do nothing, just avoid next clauses */ 415 } 416 else if (c == '(') 417 { 418 cmntcnt++; 419 c = NOCHAR; 420 } 421 else if (c == ')') 422 { 423 if (cmntcnt <= 0) 424 { 425 usrerr("Unbalanced ')'"); 426 DelimChar = p; 427 return (NULL); 428 } 429 else 430 cmntcnt--; 431 } 432 else if (cmntcnt > 0) 433 c = NOCHAR; 434 else if (c == '<') 435 anglecnt++; 436 else if (c == '>') 437 { 438 if (anglecnt <= 0) 439 { 440 usrerr("Unbalanced '>'"); 441 DelimChar = p; 442 return (NULL); 443 } 444 anglecnt--; 445 } 446 else if (delim == ' ' && isspace(c)) 447 c = ' '; 448 449 if (c == NOCHAR) 450 continue; 451 452 /* see if this is end of input */ 453 if (c == delim && anglecnt <= 0 && state != QST) 454 break; 455 456 newstate = StateTab[state][toktype(c)]; 457 if (tTd(22, 101)) 458 printf("ns=%02o\n", newstate); 459 state = newstate & TYPE; 460 if (bitset(M, newstate)) 461 c = NOCHAR; 462 if (bitset(B, newstate)) 463 break; 464 } 465 466 /* new token */ 467 if (tok != q) 468 { 469 *q++ = '\0'; 470 if (tTd(22, 36)) 471 { 472 printf("tok="); 473 xputs(tok); 474 (void) putchar('\n'); 475 } 476 if (avp >= &av[MAXATOM]) 477 { 478 syserr("prescan: too many tokens"); 479 DelimChar = p; 480 return (NULL); 481 } 482 *avp++ = tok; 483 } 484 } while (c != '\0' && (c != delim || anglecnt > 0)); 485 *avp = NULL; 486 DelimChar = --p; 487 if (tTd(22, 12)) 488 { 489 printf("prescan==>"); 490 printav(av); 491 } 492 if (av[0] != NULL) 493 return (av); 494 return (NULL); 495 } 496 /* 497 ** TOKTYPE -- return token type 498 ** 499 ** Parameters: 500 ** c -- the character in question. 501 ** 502 ** Returns: 503 ** Its type. 504 ** 505 ** Side Effects: 506 ** none. 507 */ 508 509 toktype(c) 510 register char c; 511 { 512 static char buf[50]; 513 static bool firstime = TRUE; 514 515 if (firstime) 516 { 517 firstime = FALSE; 518 expand("\001o", buf, &buf[sizeof buf - 1], CurEnv); 519 (void) strcat(buf, DELIMCHARS); 520 } 521 if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS) 522 return (ONE); 523 if (c == '"') 524 return (QST); 525 if (!isascii(c)) 526 return (ATM); 527 if (isspace(c) || c == ')') 528 return (SPC); 529 if (iscntrl(c) || strchr(buf, c) != NULL) 530 return (OPR); 531 return (ATM); 532 } 533 /* 534 ** REWRITE -- apply rewrite rules to token vector. 535 ** 536 ** This routine is an ordered production system. Each rewrite 537 ** rule has a LHS (called the pattern) and a RHS (called the 538 ** rewrite); 'rwr' points the the current rewrite rule. 539 ** 540 ** For each rewrite rule, 'avp' points the address vector we 541 ** are trying to match against, and 'pvp' points to the pattern. 542 ** If pvp points to a special match value (MATCHZANY, MATCHANY, 543 ** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp 544 ** matched is saved away in the match vector (pointed to by 'mvp'). 545 ** 546 ** When a match between avp & pvp does not match, we try to 547 ** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS 548 ** we must also back out the match in mvp. If we reach a 549 ** MATCHANY or MATCHZANY we just extend the match and start 550 ** over again. 551 ** 552 ** When we finally match, we rewrite the address vector 553 ** and try over again. 554 ** 555 ** Parameters: 556 ** pvp -- pointer to token vector. 557 ** 558 ** Returns: 559 ** none. 560 ** 561 ** Side Effects: 562 ** pvp is modified. 563 */ 564 565 struct match 566 { 567 char **first; /* first token matched */ 568 char **last; /* last token matched */ 569 }; 570 571 # define MAXMATCH 9 /* max params per rewrite */ 572 573 574 rewrite(pvp, ruleset) 575 char **pvp; 576 int ruleset; 577 { 578 register char *ap; /* address pointer */ 579 register char *rp; /* rewrite pointer */ 580 register char **avp; /* address vector pointer */ 581 register char **rvp; /* rewrite vector pointer */ 582 register struct match *mlp; /* cur ptr into mlist */ 583 register struct rewrite *rwr; /* pointer to current rewrite rule */ 584 struct match mlist[MAXMATCH]; /* stores match on LHS */ 585 char *npvp[MAXATOM+1]; /* temporary space for rebuild */ 586 587 if (OpMode == MD_TEST || tTd(21, 2)) 588 { 589 printf("rewrite: ruleset %2d input:", ruleset); 590 printav(pvp); 591 } 592 if (ruleset < 0 || ruleset >= MAXRWSETS) 593 { 594 syserr("rewrite: illegal ruleset number %d", ruleset); 595 return; 596 } 597 if (pvp == NULL) 598 return; 599 600 /* 601 ** Run through the list of rewrite rules, applying 602 ** any that match. 603 */ 604 605 for (rwr = RewriteRules[ruleset]; rwr != NULL; ) 606 { 607 int loopcount = 0; 608 609 if (tTd(21, 12)) 610 { 611 printf("-----trying rule:"); 612 printav(rwr->r_lhs); 613 } 614 615 /* try to match on this rule */ 616 mlp = mlist; 617 rvp = rwr->r_lhs; 618 avp = pvp; 619 while ((ap = *avp) != NULL || *rvp != NULL) 620 { 621 if (++loopcount > 100) 622 { 623 syserr("Infinite loop in ruleset %d", ruleset); 624 printf("workspace: "); 625 printav(pvp); 626 break; 627 } 628 rp = *rvp; 629 if (tTd(21, 35)) 630 { 631 printf("operator="); 632 xputs(ap); 633 printf(", token="); 634 xputs(rp); 635 printf("\n"); 636 } 637 if (rp == NULL) 638 { 639 /* end-of-pattern before end-of-address */ 640 goto backup; 641 } 642 if (ap == NULL && *rp != MATCHZANY) 643 { 644 /* end-of-input */ 645 break; 646 } 647 648 switch (*rp) 649 { 650 register STAB *s; 651 652 case MATCHCLASS: 653 case MATCHNCLASS: 654 /* match any token in (not in) a class */ 655 s = stab(ap, ST_CLASS, ST_FIND); 656 if (s == NULL || !bitnset(rp[1], s->s_class)) 657 { 658 if (*rp == MATCHCLASS) 659 goto backup; 660 } 661 else if (*rp == MATCHNCLASS) 662 goto backup; 663 664 /* explicit fall-through */ 665 666 case MATCHONE: 667 case MATCHANY: 668 /* match exactly one token */ 669 mlp->first = avp; 670 mlp->last = avp++; 671 mlp++; 672 break; 673 674 case MATCHZANY: 675 /* match zero or more tokens */ 676 mlp->first = avp; 677 mlp->last = avp - 1; 678 mlp++; 679 break; 680 681 default: 682 /* must have exact match */ 683 if (strcasecmp(rp, ap)) 684 goto backup; 685 avp++; 686 break; 687 } 688 689 /* successful match on this token */ 690 rvp++; 691 continue; 692 693 backup: 694 /* match failed -- back up */ 695 while (--rvp >= rwr->r_lhs) 696 { 697 rp = *rvp; 698 if (*rp == MATCHANY || *rp == MATCHZANY) 699 { 700 /* extend binding and continue */ 701 avp = ++mlp[-1].last; 702 avp++; 703 rvp++; 704 break; 705 } 706 avp--; 707 if (*rp == MATCHONE || *rp == MATCHCLASS || 708 *rp == MATCHNCLASS) 709 { 710 /* back out binding */ 711 mlp--; 712 } 713 } 714 715 if (rvp < rwr->r_lhs) 716 { 717 /* total failure to match */ 718 break; 719 } 720 } 721 722 /* 723 ** See if we successfully matched 724 */ 725 726 if (rvp < rwr->r_lhs || *rvp != NULL) 727 { 728 if (tTd(21, 10)) 729 printf("----- rule fails\n"); 730 rwr = rwr->r_next; 731 continue; 732 } 733 734 rvp = rwr->r_rhs; 735 if (tTd(21, 12)) 736 { 737 printf("-----rule matches:"); 738 printav(rvp); 739 } 740 741 rp = *rvp; 742 if (*rp == CANONUSER) 743 { 744 rvp++; 745 rwr = rwr->r_next; 746 } 747 else if (*rp == CANONHOST) 748 { 749 rvp++; 750 rwr = NULL; 751 } 752 else if (*rp == CANONNET) 753 rwr = NULL; 754 755 /* substitute */ 756 for (avp = npvp; *rvp != NULL; rvp++) 757 { 758 register struct match *m; 759 register char **pp; 760 761 rp = *rvp; 762 if (*rp == MATCHREPL) 763 { 764 /* substitute from LHS */ 765 m = &mlist[rp[1] - '1']; 766 if (m < mlist || m >= mlp) 767 { 768 syserr("rewrite: ruleset %d: replacement $%c out of bounds", 769 ruleset, rp[1]); 770 return; 771 } 772 if (tTd(21, 15)) 773 { 774 printf("$%c:", rp[1]); 775 pp = m->first; 776 while (pp <= m->last) 777 { 778 printf(" %x=\"", *pp); 779 (void) fflush(stdout); 780 printf("%s\"", *pp++); 781 } 782 printf("\n"); 783 } 784 pp = m->first; 785 while (pp <= m->last) 786 { 787 if (avp >= &npvp[MAXATOM]) 788 { 789 syserr("rewrite: expansion too long"); 790 return; 791 } 792 *avp++ = *pp++; 793 } 794 } 795 else 796 { 797 /* vanilla replacement */ 798 if (avp >= &npvp[MAXATOM]) 799 { 800 toolong: 801 syserr("rewrite: expansion too long"); 802 return; 803 } 804 *avp++ = rp; 805 } 806 } 807 *avp++ = NULL; 808 809 /* 810 ** Check for any hostname/keyword lookups. 811 */ 812 813 for (rvp = npvp; *rvp != NULL; rvp++) 814 { 815 char **hbrvp; 816 char **xpvp; 817 int trsize; 818 char *olddelimchar; 819 char *replac; 820 int endtoken; 821 STAB *map; 822 char *mapname; 823 char **key_rvp; 824 char **arg_rvp; 825 char **default_rvp; 826 char buf[MAXNAME + 1]; 827 char *pvpb1[MAXATOM + 1]; 828 char *argvect[10]; 829 char pvpbuf[PSBUFSIZE]; 830 extern char *DelimChar; 831 832 if (**rvp != HOSTBEGIN && **rvp != LOOKUPBEGIN) 833 continue; 834 835 /* 836 ** Got a hostname/keyword lookup. 837 ** 838 ** This could be optimized fairly easily. 839 */ 840 841 hbrvp = rvp; 842 if (**rvp == HOSTBEGIN) 843 { 844 endtoken = HOSTEND; 845 mapname = "host"; 846 } 847 else 848 { 849 endtoken = LOOKUPEND; 850 mapname = *++rvp; 851 } 852 map = stab(mapname, ST_MAP, ST_FIND); 853 if (map == NULL) 854 syserr("rewrite: map %s not found", mapname); 855 856 /* extract the match part */ 857 key_rvp = ++rvp; 858 default_rvp = NULL; 859 arg_rvp = argvect; 860 xpvp = NULL; 861 replac = pvpbuf; 862 while (*rvp != NULL && **rvp != endtoken) 863 { 864 int nodetype = **rvp; 865 866 if (nodetype != CANONHOST && nodetype != CANONUSER) 867 { 868 rvp++; 869 continue; 870 } 871 872 *rvp++ = NULL; 873 874 if (xpvp != NULL) 875 { 876 cataddr(xpvp, replac, 877 &pvpbuf[sizeof pvpbuf] - replac); 878 *++arg_rvp = replac; 879 replac += strlen(replac) + 1; 880 xpvp = NULL; 881 } 882 switch (nodetype) 883 { 884 case CANONHOST: 885 xpvp = rvp; 886 break; 887 888 case CANONUSER: 889 default_rvp = rvp; 890 break; 891 } 892 } 893 if (*rvp != NULL) 894 *rvp++ = NULL; 895 if (xpvp != NULL) 896 { 897 cataddr(xpvp, replac, 898 &pvpbuf[sizeof pvpbuf] - replac); 899 *++arg_rvp = replac; 900 } 901 *++arg_rvp = NULL; 902 903 /* save the remainder of the input string */ 904 trsize = (int) (avp - rvp + 1) * sizeof *rvp; 905 bcopy((char *) rvp, (char *) pvpb1, trsize); 906 907 /* look it up */ 908 cataddr(key_rvp, buf, sizeof buf); 909 argvect[0] = buf; 910 if (map != NULL && bitset(MF_VALID, map->s_map.map_flags)) 911 { 912 int bsize = sizeof buf - 1; 913 914 if (map->s_map.map_app != NULL) 915 bsize -= strlen(map->s_map.map_app); 916 replac = (*map->s_map.map_class->map_lookup)(&map->s_map, 917 buf, sizeof buf - 1, argvect); 918 if (replac != NULL && map->s_map.map_app != NULL) 919 strcat(replac, map->s_map.map_app); 920 } 921 else 922 replac = NULL; 923 924 /* if no replacement, use default */ 925 if (replac == NULL && default_rvp != NULL) 926 { 927 char buf2[sizeof buf]; 928 929 /* rewrite the default with % translations */ 930 cataddr(default_rvp, buf2, sizeof buf2); 931 map_rewrite(buf2, sizeof buf2, buf, sizeof buf, 932 argvect); 933 replac = buf; 934 } 935 936 if (replac == NULL) 937 { 938 xpvp = key_rvp; 939 } 940 else 941 { 942 /* scan the new replacement */ 943 olddelimchar = DelimChar; 944 xpvp = prescan(replac, '\0', pvpbuf); 945 DelimChar = olddelimchar; 946 if (xpvp == NULL) 947 { 948 syserr("rewrite: cannot prescan map value: %s", replac); 949 return; 950 } 951 } 952 953 /* append it to the token list */ 954 for (avp = hbrvp; *xpvp != NULL; xpvp++) 955 { 956 *avp++ = newstr(*xpvp); 957 if (avp >= &npvp[MAXATOM]) 958 goto toolong; 959 } 960 961 /* restore the old trailing information */ 962 for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) 963 if (avp >= &npvp[MAXATOM]) 964 goto toolong; 965 966 break; 967 } 968 969 /* 970 ** Check for subroutine calls. 971 */ 972 973 if (*npvp != NULL && **npvp == CALLSUBR) 974 { 975 bcopy((char *) &npvp[2], (char *) pvp, 976 (int) (avp - npvp - 2) * sizeof *avp); 977 if (tTd(21, 3)) 978 printf("-----callsubr %s\n", npvp[1]); 979 rewrite(pvp, atoi(npvp[1])); 980 } 981 else 982 { 983 bcopy((char *) npvp, (char *) pvp, 984 (int) (avp - npvp) * sizeof *avp); 985 } 986 if (tTd(21, 4)) 987 { 988 printf("rewritten as:"); 989 printav(pvp); 990 } 991 } 992 993 if (OpMode == MD_TEST || tTd(21, 2)) 994 { 995 printf("rewrite: ruleset %2d returns:", ruleset); 996 printav(pvp); 997 } 998 } 999 /* 1000 ** BUILDADDR -- build address from token vector. 1001 ** 1002 ** Parameters: 1003 ** tv -- token vector. 1004 ** a -- pointer to address descriptor to fill. 1005 ** If NULL, one will be allocated. 1006 ** 1007 ** Returns: 1008 ** NULL if there was an error. 1009 ** 'a' otherwise. 1010 ** 1011 ** Side Effects: 1012 ** fills in 'a' 1013 */ 1014 1015 struct errcodes 1016 { 1017 char *ec_name; /* name of error code */ 1018 int ec_code; /* numeric code */ 1019 } ErrorCodes[] = 1020 { 1021 "usage", EX_USAGE, 1022 "nouser", EX_NOUSER, 1023 "nohost", EX_NOHOST, 1024 "unavailable", EX_UNAVAILABLE, 1025 "software", EX_SOFTWARE, 1026 "tempfail", EX_TEMPFAIL, 1027 "protocol", EX_PROTOCOL, 1028 #ifdef EX_CONFIG 1029 "config", EX_CONFIG, 1030 #endif 1031 NULL, EX_UNAVAILABLE, 1032 }; 1033 1034 ADDRESS * 1035 buildaddr(tv, a) 1036 register char **tv; 1037 register ADDRESS *a; 1038 { 1039 struct mailer **mp; 1040 register struct mailer *m; 1041 static char buf[MAXNAME]; 1042 1043 if (a == NULL) 1044 a = (ADDRESS *) xalloc(sizeof *a); 1045 bzero((char *) a, sizeof *a); 1046 1047 /* figure out what net/mailer to use */ 1048 if (**tv != CANONNET) 1049 { 1050 syserr("buildaddr: no net"); 1051 return (NULL); 1052 } 1053 tv++; 1054 if (!strcasecmp(*tv, "error")) 1055 { 1056 if (**++tv == CANONHOST) 1057 { 1058 register struct errcodes *ep; 1059 1060 if (isdigit(**++tv)) 1061 { 1062 setstat(atoi(*tv)); 1063 } 1064 else 1065 { 1066 for (ep = ErrorCodes; ep->ec_name != NULL; ep++) 1067 if (strcasecmp(ep->ec_name, *tv) == 0) 1068 break; 1069 setstat(ep->ec_code); 1070 } 1071 tv++; 1072 } 1073 if (**tv != CANONUSER) 1074 syserr("buildaddr: error: no user"); 1075 buf[0] = '\0'; 1076 while (*++tv != NULL) 1077 { 1078 if (buf[0] != '\0') 1079 (void) strcat(buf, " "); 1080 (void) strcat(buf, *tv); 1081 } 1082 usrerr(buf); 1083 return (NULL); 1084 } 1085 1086 for (mp = Mailer; (m = *mp++) != NULL; ) 1087 { 1088 if (!strcasecmp(m->m_name, *tv)) 1089 break; 1090 } 1091 if (m == NULL) 1092 { 1093 syserr("buildaddr: unknown mailer %s", *tv); 1094 return (NULL); 1095 } 1096 a->q_mailer = m; 1097 1098 /* figure out what host (if any) */ 1099 tv++; 1100 if (!bitnset(M_LOCAL, m->m_flags)) 1101 { 1102 if (**tv++ != CANONHOST) 1103 { 1104 syserr("buildaddr: no host"); 1105 return (NULL); 1106 } 1107 buf[0] = '\0'; 1108 while (*tv != NULL && **tv != CANONUSER) 1109 (void) strcat(buf, *tv++); 1110 a->q_host = newstr(buf); 1111 } 1112 else 1113 a->q_host = NULL; 1114 1115 /* figure out the user */ 1116 if (*tv == NULL || **tv != CANONUSER) 1117 { 1118 syserr("buildaddr: no user"); 1119 return (NULL); 1120 } 1121 1122 if (m == LocalMailer && tv[1] != NULL && strcmp(tv[1], "@") == 0) 1123 { 1124 tv++; 1125 a->q_flags |= QNOTREMOTE; 1126 } 1127 tv++; 1128 1129 /* do special mapping for local mailer */ 1130 if (m == LocalMailer && *tv != NULL) 1131 { 1132 register char *p = *tv; 1133 1134 if (*p == '"') 1135 p++; 1136 if (*p == '|') 1137 a->q_mailer = m = ProgMailer; 1138 else if (*p == '/') 1139 a->q_mailer = m = FileMailer; 1140 else if (*p == ':') 1141 { 1142 /* may be :include: */ 1143 cataddr(tv, buf, sizeof buf); 1144 p = buf; 1145 if (*p == '"') 1146 p++; 1147 p[9] = '\0'; 1148 if (strcasecmp(p, ":include:") == 0) 1149 a->q_mailer = m = InclMailer; 1150 } 1151 } 1152 1153 /* rewrite according recipient mailer rewriting rules */ 1154 rewrite(tv, 2); 1155 if (m->m_r_rwset > 0) 1156 rewrite(tv, m->m_r_rwset); 1157 rewrite(tv, 4); 1158 1159 /* save the result for the command line/RCPT argument */ 1160 cataddr(tv, buf, sizeof buf); 1161 a->q_user = buf; 1162 1163 return (a); 1164 } 1165 /* 1166 ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 1167 ** 1168 ** Parameters: 1169 ** pvp -- parameter vector to rebuild. 1170 ** buf -- buffer to build the string into. 1171 ** sz -- size of buf. 1172 ** 1173 ** Returns: 1174 ** none. 1175 ** 1176 ** Side Effects: 1177 ** Destroys buf. 1178 */ 1179 1180 cataddr(pvp, buf, sz) 1181 char **pvp; 1182 char *buf; 1183 register int sz; 1184 { 1185 bool oatomtok = FALSE; 1186 bool natomtok = FALSE; 1187 register int i; 1188 register char *p; 1189 1190 if (pvp == NULL) 1191 { 1192 (void) strcpy(buf, ""); 1193 return; 1194 } 1195 p = buf; 1196 sz -= 2; 1197 while (*pvp != NULL && (i = strlen(*pvp)) < sz) 1198 { 1199 natomtok = (toktype(**pvp) == ATM); 1200 if (oatomtok && natomtok) 1201 *p++ = SpaceSub; 1202 (void) strcpy(p, *pvp); 1203 oatomtok = natomtok; 1204 p += i; 1205 sz -= i + 1; 1206 pvp++; 1207 } 1208 *p = '\0'; 1209 } 1210 /* 1211 ** SAMEADDR -- Determine if two addresses are the same 1212 ** 1213 ** This is not just a straight comparison -- if the mailer doesn't 1214 ** care about the host we just ignore it, etc. 1215 ** 1216 ** Parameters: 1217 ** a, b -- pointers to the internal forms to compare. 1218 ** 1219 ** Returns: 1220 ** TRUE -- they represent the same mailbox. 1221 ** FALSE -- they don't. 1222 ** 1223 ** Side Effects: 1224 ** none. 1225 */ 1226 1227 bool 1228 sameaddr(a, b) 1229 register ADDRESS *a; 1230 register ADDRESS *b; 1231 { 1232 /* if they don't have the same mailer, forget it */ 1233 if (a->q_mailer != b->q_mailer) 1234 return (FALSE); 1235 1236 /* if the user isn't the same, we can drop out */ 1237 if (strcmp(a->q_user, b->q_user) != 0) 1238 return (FALSE); 1239 1240 /* if the mailer ignores hosts, we have succeeded! */ 1241 if (bitnset(M_LOCAL, a->q_mailer->m_flags)) 1242 return (TRUE); 1243 1244 /* otherwise compare hosts (but be careful for NULL ptrs) */ 1245 if (a->q_host == NULL || b->q_host == NULL) 1246 return (FALSE); 1247 if (strcmp(a->q_host, b->q_host) != 0) 1248 return (FALSE); 1249 1250 return (TRUE); 1251 } 1252 /* 1253 ** PRINTADDR -- print address (for debugging) 1254 ** 1255 ** Parameters: 1256 ** a -- the address to print 1257 ** follow -- follow the q_next chain. 1258 ** 1259 ** Returns: 1260 ** none. 1261 ** 1262 ** Side Effects: 1263 ** none. 1264 */ 1265 1266 printaddr(a, follow) 1267 register ADDRESS *a; 1268 bool follow; 1269 { 1270 bool first = TRUE; 1271 1272 while (a != NULL) 1273 { 1274 first = FALSE; 1275 printf("%x=", a); 1276 (void) fflush(stdout); 1277 printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n", 1278 a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name, 1279 a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>"); 1280 printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags, 1281 a->q_alias); 1282 printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home, 1283 a->q_fullname); 1284 1285 if (!follow) 1286 return; 1287 a = a->q_next; 1288 } 1289 if (first) 1290 printf("[NULL]\n"); 1291 } 1292 1293 /* 1294 ** REMOTENAME -- return the name relative to the current mailer 1295 ** 1296 ** Parameters: 1297 ** name -- the name to translate. 1298 ** m -- the mailer that we want to do rewriting relative 1299 ** to. 1300 ** senderaddress -- if set, uses the sender rewriting rules 1301 ** rather than the recipient rewriting rules. 1302 ** canonical -- if set, strip out any comment information, 1303 ** etc. 1304 ** 1305 ** Returns: 1306 ** the text string representing this address relative to 1307 ** the receiving mailer. 1308 ** 1309 ** Side Effects: 1310 ** none. 1311 ** 1312 ** Warnings: 1313 ** The text string returned is tucked away locally; 1314 ** copy it if you intend to save it. 1315 */ 1316 1317 char * 1318 remotename(name, m, senderaddress, canonical, e) 1319 char *name; 1320 struct mailer *m; 1321 bool senderaddress; 1322 bool canonical; 1323 register ENVELOPE *e; 1324 { 1325 register char **pvp; 1326 char *fancy; 1327 extern char *macvalue(); 1328 char *oldg = macvalue('g', e); 1329 static char buf[MAXNAME]; 1330 char lbuf[MAXNAME]; 1331 char pvpbuf[PSBUFSIZE]; 1332 extern char **prescan(); 1333 extern char *crackaddr(); 1334 1335 if (tTd(12, 1)) 1336 printf("remotename(%s)\n", name); 1337 1338 /* don't do anything if we are tagging it as special */ 1339 if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0) 1340 return (name); 1341 1342 /* 1343 ** Do a heuristic crack of this name to extract any comment info. 1344 ** This will leave the name as a comment and a $g macro. 1345 */ 1346 1347 if (canonical) 1348 fancy = "\001g"; 1349 else 1350 fancy = crackaddr(name); 1351 1352 /* 1353 ** Turn the name into canonical form. 1354 ** Normally this will be RFC 822 style, i.e., "user@domain". 1355 ** If this only resolves to "user", and the "C" flag is 1356 ** specified in the sending mailer, then the sender's 1357 ** domain will be appended. 1358 */ 1359 1360 pvp = prescan(name, '\0', pvpbuf); 1361 if (pvp == NULL) 1362 return (name); 1363 rewrite(pvp, 3); 1364 if (e->e_fromdomain != NULL) 1365 { 1366 /* append from domain to this address */ 1367 register char **pxp = pvp; 1368 1369 /* see if there is an "@domain" in the current name */ 1370 while (*pxp != NULL && strcmp(*pxp, "@") != 0) 1371 pxp++; 1372 if (*pxp == NULL) 1373 { 1374 /* no.... append the "@domain" from the sender */ 1375 register char **qxq = e->e_fromdomain; 1376 1377 while ((*pxp++ = *qxq++) != NULL) 1378 continue; 1379 rewrite(pvp, 3); 1380 } 1381 } 1382 1383 /* 1384 ** Do more specific rewriting. 1385 ** Rewrite using ruleset 1 or 2 depending on whether this is 1386 ** a sender address or not. 1387 ** Then run it through any receiving-mailer-specific rulesets. 1388 */ 1389 1390 if (senderaddress) 1391 { 1392 rewrite(pvp, 1); 1393 if (m->m_s_rwset > 0) 1394 rewrite(pvp, m->m_s_rwset); 1395 } 1396 else 1397 { 1398 rewrite(pvp, 2); 1399 if (m->m_r_rwset > 0) 1400 rewrite(pvp, m->m_r_rwset); 1401 } 1402 1403 /* 1404 ** Do any final sanitation the address may require. 1405 ** This will normally be used to turn internal forms 1406 ** (e.g., user@host.LOCAL) into external form. This 1407 ** may be used as a default to the above rules. 1408 */ 1409 1410 rewrite(pvp, 4); 1411 1412 /* 1413 ** Now restore the comment information we had at the beginning. 1414 */ 1415 1416 cataddr(pvp, lbuf, sizeof lbuf); 1417 define('g', lbuf, e); 1418 expand(fancy, buf, &buf[sizeof buf - 1], e); 1419 define('g', oldg, e); 1420 1421 if (tTd(12, 1)) 1422 printf("remotename => `%s'\n", buf); 1423 return (buf); 1424 } 1425 /* 1426 ** MAPLOCALUSER -- run local username through ruleset 5 for final redirection 1427 ** 1428 ** Parameters: 1429 ** a -- the address to map (but just the user name part). 1430 ** sendq -- the sendq in which to install any replacement 1431 ** addresses. 1432 ** 1433 ** Returns: 1434 ** none. 1435 */ 1436 1437 maplocaluser(a, sendq, e) 1438 register ADDRESS *a; 1439 ADDRESS **sendq; 1440 ENVELOPE *e; 1441 { 1442 register char **pvp; 1443 register ADDRESS *a1 = NULL; 1444 char pvpbuf[PSBUFSIZE]; 1445 1446 if (tTd(29, 1)) 1447 { 1448 printf("maplocaluser: "); 1449 printaddr(a, FALSE); 1450 } 1451 pvp = prescan(a->q_user, '\0', pvpbuf); 1452 if (pvp == NULL) 1453 return; 1454 1455 rewrite(pvp, 5); 1456 if (pvp[0] == NULL || pvp[0][0] != CANONNET) 1457 return; 1458 1459 /* if non-null, mailer destination specified -- has it changed? */ 1460 a1 = buildaddr(pvp, NULL); 1461 if (a1 == NULL || sameaddr(a, a1)) 1462 return; 1463 1464 /* mark old address as dead; insert new address */ 1465 a->q_flags |= QDONTSEND; 1466 a1->q_alias = a; 1467 allocaddr(a1, 1, NULL); 1468 (void) recipient(a1, sendq, e); 1469 } 1470