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.19 (Berkeley) 02/23/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 syserr("554 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("553 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("553 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("553 Unbalanced '\"'"); 372 c = '"'; 373 } 374 else if (cmntcnt > 0) 375 { 376 usrerr("553 Unbalanced '('"); 377 c = ')'; 378 } 379 else if (anglecnt > 0) 380 { 381 c = '>'; 382 usrerr("553 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("553 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("553 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("553 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("554 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("554 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("554 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("554 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("554 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("554 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 '\0'); 888 *++arg_rvp = replac; 889 replac += strlen(replac) + 1; 890 xpvp = NULL; 891 } 892 switch (nodetype) 893 { 894 case CANONHOST: 895 xpvp = rvp; 896 break; 897 898 case CANONUSER: 899 default_rvp = rvp; 900 break; 901 } 902 } 903 if (*rvp != NULL) 904 *rvp++ = NULL; 905 if (xpvp != NULL) 906 { 907 cataddr(xpvp, replac, 908 &pvpbuf[sizeof pvpbuf] - replac, 909 '\0'); 910 *++arg_rvp = replac; 911 } 912 *++arg_rvp = NULL; 913 914 /* save the remainder of the input string */ 915 trsize = (int) (avp - rvp + 1) * sizeof *rvp; 916 bcopy((char *) rvp, (char *) pvpb1, trsize); 917 918 /* look it up */ 919 cataddr(key_rvp, buf, sizeof buf, '\0'); 920 argvect[0] = buf; 921 if (map != NULL && bitset(MF_VALID, map->s_map.map_flags)) 922 { 923 int bsize = sizeof buf - 1; 924 925 if (map->s_map.map_app != NULL) 926 bsize -= strlen(map->s_map.map_app); 927 replac = (*map->s_map.map_class->map_lookup)(&map->s_map, 928 buf, sizeof buf - 1, argvect); 929 if (replac != NULL && map->s_map.map_app != NULL) 930 strcat(replac, map->s_map.map_app); 931 } 932 else 933 replac = NULL; 934 935 /* if no replacement, use default */ 936 if (replac == NULL && default_rvp != NULL) 937 { 938 char buf2[sizeof buf]; 939 940 /* rewrite the default with % translations */ 941 cataddr(default_rvp, buf2, sizeof buf2, '\0'); 942 map_rewrite(buf2, sizeof buf2, buf, sizeof buf, 943 argvect); 944 replac = buf; 945 } 946 947 if (replac == NULL) 948 { 949 xpvp = key_rvp; 950 } 951 else 952 { 953 /* scan the new replacement */ 954 olddelimchar = DelimChar; 955 xpvp = prescan(replac, '\0', pvpbuf); 956 DelimChar = olddelimchar; 957 if (xpvp == NULL) 958 { 959 syserr("553 rewrite: cannot prescan map value: %s", replac); 960 return; 961 } 962 } 963 964 /* append it to the token list */ 965 for (avp = hbrvp; *xpvp != NULL; xpvp++) 966 { 967 *avp++ = newstr(*xpvp); 968 if (avp >= &npvp[MAXATOM]) 969 goto toolong; 970 } 971 972 /* restore the old trailing information */ 973 for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) 974 if (avp >= &npvp[MAXATOM]) 975 goto toolong; 976 977 break; 978 } 979 980 /* 981 ** Check for subroutine calls. 982 */ 983 984 if (*npvp != NULL && (**npvp & 0377) == CALLSUBR) 985 { 986 bcopy((char *) &npvp[2], (char *) pvp, 987 (int) (avp - npvp - 2) * sizeof *avp); 988 if (tTd(21, 3)) 989 printf("-----callsubr %s\n", npvp[1]); 990 rewrite(pvp, atoi(npvp[1])); 991 } 992 else 993 { 994 bcopy((char *) npvp, (char *) pvp, 995 (int) (avp - npvp) * sizeof *avp); 996 } 997 if (tTd(21, 4)) 998 { 999 printf("rewritten as:"); 1000 printav(pvp); 1001 } 1002 } 1003 1004 if (OpMode == MD_TEST || tTd(21, 2)) 1005 { 1006 printf("rewrite: ruleset %2d returns:", ruleset); 1007 printav(pvp); 1008 } 1009 } 1010 /* 1011 ** BUILDADDR -- build address from token vector. 1012 ** 1013 ** Parameters: 1014 ** tv -- token vector. 1015 ** a -- pointer to address descriptor to fill. 1016 ** If NULL, one will be allocated. 1017 ** 1018 ** Returns: 1019 ** NULL if there was an error. 1020 ** 'a' otherwise. 1021 ** 1022 ** Side Effects: 1023 ** fills in 'a' 1024 */ 1025 1026 struct errcodes 1027 { 1028 char *ec_name; /* name of error code */ 1029 int ec_code; /* numeric code */ 1030 } ErrorCodes[] = 1031 { 1032 "usage", EX_USAGE, 1033 "nouser", EX_NOUSER, 1034 "nohost", EX_NOHOST, 1035 "unavailable", EX_UNAVAILABLE, 1036 "software", EX_SOFTWARE, 1037 "tempfail", EX_TEMPFAIL, 1038 "protocol", EX_PROTOCOL, 1039 #ifdef EX_CONFIG 1040 "config", EX_CONFIG, 1041 #endif 1042 NULL, EX_UNAVAILABLE, 1043 }; 1044 1045 ADDRESS * 1046 buildaddr(tv, a) 1047 register char **tv; 1048 register ADDRESS *a; 1049 { 1050 struct mailer **mp; 1051 register struct mailer *m; 1052 char *bp; 1053 int spaceleft; 1054 static char buf[MAXNAME]; 1055 1056 if (a == NULL) 1057 a = (ADDRESS *) xalloc(sizeof *a); 1058 bzero((char *) a, sizeof *a); 1059 1060 /* figure out what net/mailer to use */ 1061 if ((**tv & 0377) != CANONNET) 1062 { 1063 syserr("554 buildaddr: no net"); 1064 return (NULL); 1065 } 1066 tv++; 1067 if (!strcasecmp(*tv, "error")) 1068 { 1069 if ((**++tv & 0377) == CANONHOST) 1070 { 1071 register struct errcodes *ep; 1072 1073 if (isascii(**++tv) && isdigit(**tv)) 1074 { 1075 setstat(atoi(*tv)); 1076 } 1077 else 1078 { 1079 for (ep = ErrorCodes; ep->ec_name != NULL; ep++) 1080 if (strcasecmp(ep->ec_name, *tv) == 0) 1081 break; 1082 setstat(ep->ec_code); 1083 } 1084 tv++; 1085 } 1086 if ((**tv & 0377) != CANONUSER) 1087 syserr("554 buildaddr: error: no user"); 1088 cataddr(++tv, buf, sizeof buf, ' '); 1089 stripquotes(buf); 1090 usrerr(buf); 1091 return (NULL); 1092 } 1093 1094 for (mp = Mailer; (m = *mp++) != NULL; ) 1095 { 1096 if (!strcasecmp(m->m_name, *tv)) 1097 break; 1098 } 1099 if (m == NULL) 1100 { 1101 syserr("554 buildaddr: unknown mailer %s", *tv); 1102 return (NULL); 1103 } 1104 a->q_mailer = m; 1105 1106 /* figure out what host (if any) */ 1107 tv++; 1108 if (!bitnset(M_LOCALMAILER, m->m_flags)) 1109 { 1110 if ((**tv & 0377) != CANONHOST) 1111 { 1112 syserr("554 buildaddr: no host"); 1113 return (NULL); 1114 } 1115 bp = buf; 1116 spaceleft = sizeof buf - 1; 1117 while (*++tv != NULL && (**tv & 0377) != CANONUSER) 1118 { 1119 int i = strlen(*tv); 1120 1121 if (i > spaceleft) 1122 { 1123 /* out of space for this address */ 1124 if (spaceleft >= 0) 1125 syserr("554 buildaddr: host too long (%.40s...)", 1126 buf); 1127 i = spaceleft; 1128 spaceleft = 0; 1129 } 1130 if (i <= 0) 1131 continue; 1132 bcopy(*tv, bp, i); 1133 bp += i; 1134 spaceleft -= i; 1135 } 1136 *bp = '\0'; 1137 a->q_host = newstr(buf); 1138 } 1139 else 1140 a->q_host = NULL; 1141 1142 /* figure out the user */ 1143 if (*tv == NULL || (**tv & 0377) != CANONUSER) 1144 { 1145 syserr("554 buildaddr: no user"); 1146 return (NULL); 1147 } 1148 tv++; 1149 1150 /* do special mapping for local mailer */ 1151 if (m == LocalMailer && *tv != NULL) 1152 { 1153 register char *p = *tv; 1154 1155 if (*p == '"') 1156 p++; 1157 if (*p == '|') 1158 a->q_mailer = m = ProgMailer; 1159 else if (*p == '/') 1160 a->q_mailer = m = FileMailer; 1161 else if (*p == ':') 1162 { 1163 /* may be :include: */ 1164 cataddr(tv, buf, sizeof buf, '\0'); 1165 stripquotes(buf); 1166 if (strncasecmp(buf, ":include:", 9) == 0) 1167 { 1168 /* if :include:, don't need further rewriting */ 1169 a->q_mailer = m = InclMailer; 1170 a->q_user = &buf[9]; 1171 return (a); 1172 } 1173 } 1174 } 1175 1176 if (m == LocalMailer && *tv != NULL && strcmp(*tv, "@") == 0) 1177 { 1178 tv++; 1179 a->q_flags |= QNOTREMOTE; 1180 } 1181 1182 /* rewrite according recipient mailer rewriting rules */ 1183 rewrite(tv, 2); 1184 if (m->m_re_rwset > 0) 1185 rewrite(tv, m->m_re_rwset); 1186 rewrite(tv, 4); 1187 1188 /* save the result for the command line/RCPT argument */ 1189 cataddr(tv, buf, sizeof buf, '\0'); 1190 a->q_user = buf; 1191 1192 return (a); 1193 } 1194 /* 1195 ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 1196 ** 1197 ** Parameters: 1198 ** pvp -- parameter vector to rebuild. 1199 ** buf -- buffer to build the string into. 1200 ** sz -- size of buf. 1201 ** spacesub -- the space separator character; if null, 1202 ** use SpaceSub. 1203 ** 1204 ** Returns: 1205 ** none. 1206 ** 1207 ** Side Effects: 1208 ** Destroys buf. 1209 */ 1210 1211 cataddr(pvp, buf, sz, spacesub) 1212 char **pvp; 1213 char *buf; 1214 register int sz; 1215 char spacesub; 1216 { 1217 bool oatomtok = FALSE; 1218 bool natomtok = FALSE; 1219 register int i; 1220 register char *p; 1221 1222 if (spacesub == '\0') 1223 spacesub = SpaceSub; 1224 1225 if (pvp == NULL) 1226 { 1227 (void) strcpy(buf, ""); 1228 return; 1229 } 1230 p = buf; 1231 sz -= 2; 1232 while (*pvp != NULL && (i = strlen(*pvp)) < sz) 1233 { 1234 natomtok = (toktype(**pvp) == ATM); 1235 if (oatomtok && natomtok) 1236 *p++ = spacesub; 1237 (void) strcpy(p, *pvp); 1238 oatomtok = natomtok; 1239 p += i; 1240 sz -= i + 1; 1241 pvp++; 1242 } 1243 *p = '\0'; 1244 } 1245 /* 1246 ** SAMEADDR -- Determine if two addresses are the same 1247 ** 1248 ** This is not just a straight comparison -- if the mailer doesn't 1249 ** care about the host we just ignore it, etc. 1250 ** 1251 ** Parameters: 1252 ** a, b -- pointers to the internal forms to compare. 1253 ** 1254 ** Returns: 1255 ** TRUE -- they represent the same mailbox. 1256 ** FALSE -- they don't. 1257 ** 1258 ** Side Effects: 1259 ** none. 1260 */ 1261 1262 bool 1263 sameaddr(a, b) 1264 register ADDRESS *a; 1265 register ADDRESS *b; 1266 { 1267 /* if they don't have the same mailer, forget it */ 1268 if (a->q_mailer != b->q_mailer) 1269 return (FALSE); 1270 1271 /* if the user isn't the same, we can drop out */ 1272 if (strcmp(a->q_user, b->q_user) != 0) 1273 return (FALSE); 1274 1275 /* if the mailer ignores hosts, we have succeeded! */ 1276 if (bitnset(M_LOCALMAILER, a->q_mailer->m_flags)) 1277 return (TRUE); 1278 1279 /* otherwise compare hosts (but be careful for NULL ptrs) */ 1280 if (a->q_host == NULL || b->q_host == NULL) 1281 return (FALSE); 1282 if (strcmp(a->q_host, b->q_host) != 0) 1283 return (FALSE); 1284 1285 return (TRUE); 1286 } 1287 /* 1288 ** PRINTADDR -- print address (for debugging) 1289 ** 1290 ** Parameters: 1291 ** a -- the address to print 1292 ** follow -- follow the q_next chain. 1293 ** 1294 ** Returns: 1295 ** none. 1296 ** 1297 ** Side Effects: 1298 ** none. 1299 */ 1300 1301 printaddr(a, follow) 1302 register ADDRESS *a; 1303 bool follow; 1304 { 1305 bool first = TRUE; 1306 register MAILER *m; 1307 MAILER pseudomailer; 1308 1309 while (a != NULL) 1310 { 1311 first = FALSE; 1312 printf("%x=", a); 1313 (void) fflush(stdout); 1314 1315 /* find the mailer -- carefully */ 1316 m = a->q_mailer; 1317 if (m == NULL) 1318 { 1319 m = &pseudomailer; 1320 m->m_mno = -1; 1321 m->m_name = "NULL"; 1322 } 1323 1324 printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n", 1325 a->q_paddr, m->m_mno, m->m_name, 1326 a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>"); 1327 printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags, 1328 a->q_alias); 1329 printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home, 1330 a->q_fullname); 1331 1332 if (!follow) 1333 return; 1334 a = a->q_next; 1335 } 1336 if (first) 1337 printf("[NULL]\n"); 1338 } 1339 1340 /* 1341 ** REMOTENAME -- return the name relative to the current mailer 1342 ** 1343 ** Parameters: 1344 ** name -- the name to translate. 1345 ** m -- the mailer that we want to do rewriting relative 1346 ** to. 1347 ** senderaddress -- if set, uses the sender rewriting rules 1348 ** rather than the recipient rewriting rules. 1349 ** header -- set if this address is in the header, rather 1350 ** than an envelope header. 1351 ** canonical -- if set, strip out any comment information, 1352 ** etc. 1353 ** e -- the current envelope. 1354 ** 1355 ** Returns: 1356 ** the text string representing this address relative to 1357 ** the receiving mailer. 1358 ** 1359 ** Side Effects: 1360 ** none. 1361 ** 1362 ** Warnings: 1363 ** The text string returned is tucked away locally; 1364 ** copy it if you intend to save it. 1365 */ 1366 1367 char * 1368 remotename(name, m, senderaddress, header, canonical, e) 1369 char *name; 1370 struct mailer *m; 1371 bool senderaddress; 1372 bool header; 1373 bool canonical; 1374 register ENVELOPE *e; 1375 { 1376 register char **pvp; 1377 char *fancy; 1378 extern char *macvalue(); 1379 char *oldg = macvalue('g', e); 1380 int rwset; 1381 static char buf[MAXNAME]; 1382 char lbuf[MAXNAME]; 1383 char pvpbuf[PSBUFSIZE]; 1384 extern char **prescan(); 1385 extern char *crackaddr(); 1386 1387 if (tTd(12, 1)) 1388 printf("remotename(%s)\n", name); 1389 1390 /* don't do anything if we are tagging it as special */ 1391 if (senderaddress) 1392 rwset = header ? m->m_sh_rwset : m->m_se_rwset; 1393 else 1394 rwset = header ? m->m_rh_rwset : m->m_re_rwset; 1395 if (rwset < 0) 1396 return (name); 1397 1398 /* 1399 ** Do a heuristic crack of this name to extract any comment info. 1400 ** This will leave the name as a comment and a $g macro. 1401 */ 1402 1403 if (canonical || bitnset(M_NOCOMMENT, m->m_flags)) 1404 fancy = "\201g"; 1405 else 1406 fancy = crackaddr(name); 1407 1408 /* 1409 ** Turn the name into canonical form. 1410 ** Normally this will be RFC 822 style, i.e., "user@domain". 1411 ** If this only resolves to "user", and the "C" flag is 1412 ** specified in the sending mailer, then the sender's 1413 ** domain will be appended. 1414 */ 1415 1416 pvp = prescan(name, '\0', pvpbuf); 1417 if (pvp == NULL) 1418 return (name); 1419 rewrite(pvp, 3); 1420 if (e->e_fromdomain != NULL) 1421 { 1422 /* append from domain to this address */ 1423 register char **pxp = pvp; 1424 1425 /* see if there is an "@domain" in the current name */ 1426 while (*pxp != NULL && strcmp(*pxp, "@") != 0) 1427 pxp++; 1428 if (*pxp == NULL) 1429 { 1430 /* no.... append the "@domain" from the sender */ 1431 register char **qxq = e->e_fromdomain; 1432 1433 while ((*pxp++ = *qxq++) != NULL) 1434 continue; 1435 rewrite(pvp, 3); 1436 } 1437 } 1438 1439 /* 1440 ** Do more specific rewriting. 1441 ** Rewrite using ruleset 1 or 2 depending on whether this is 1442 ** a sender address or not. 1443 ** Then run it through any receiving-mailer-specific rulesets. 1444 */ 1445 1446 if (senderaddress) 1447 rewrite(pvp, 1); 1448 else 1449 rewrite(pvp, 2); 1450 if (rwset > 0) 1451 rewrite(pvp, rwset); 1452 1453 /* 1454 ** Do any final sanitation the address may require. 1455 ** This will normally be used to turn internal forms 1456 ** (e.g., user@host.LOCAL) into external form. This 1457 ** may be used as a default to the above rules. 1458 */ 1459 1460 rewrite(pvp, 4); 1461 1462 /* 1463 ** Now restore the comment information we had at the beginning. 1464 */ 1465 1466 cataddr(pvp, lbuf, sizeof lbuf, '\0'); 1467 define('g', lbuf, e); 1468 expand(fancy, buf, &buf[sizeof buf - 1], e); 1469 define('g', oldg, e); 1470 1471 if (tTd(12, 1)) 1472 printf("remotename => `%s'\n", buf); 1473 return (buf); 1474 } 1475 /* 1476 ** MAPLOCALUSER -- run local username through ruleset 5 for final redirection 1477 ** 1478 ** Parameters: 1479 ** a -- the address to map (but just the user name part). 1480 ** sendq -- the sendq in which to install any replacement 1481 ** addresses. 1482 ** 1483 ** Returns: 1484 ** none. 1485 */ 1486 1487 maplocaluser(a, sendq, e) 1488 register ADDRESS *a; 1489 ADDRESS **sendq; 1490 ENVELOPE *e; 1491 { 1492 register char **pvp; 1493 register ADDRESS *a1 = NULL; 1494 char pvpbuf[PSBUFSIZE]; 1495 1496 if (tTd(29, 1)) 1497 { 1498 printf("maplocaluser: "); 1499 printaddr(a, FALSE); 1500 } 1501 pvp = prescan(a->q_user, '\0', pvpbuf); 1502 if (pvp == NULL) 1503 return; 1504 1505 rewrite(pvp, 5); 1506 if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) 1507 return; 1508 1509 /* if non-null, mailer destination specified -- has it changed? */ 1510 a1 = buildaddr(pvp, NULL); 1511 if (a1 == NULL || sameaddr(a, a1)) 1512 return; 1513 1514 /* mark old address as dead; insert new address */ 1515 a->q_flags |= QDONTSEND; 1516 if (tTd(29, 5)) 1517 { 1518 printf("maplocaluser: QDONTSEND "); 1519 printaddr(a, FALSE); 1520 } 1521 a1->q_alias = a; 1522 allocaddr(a1, 1, NULL); 1523 (void) recipient(a1, sendq, e); 1524 } 1525