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