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