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