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