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.18 (Berkeley) 05/25/92"; 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 345 if (c == '\\') 346 { 347 bslashmode = TRUE; 348 c = NOCHAR; 349 } 350 else if (state == QST) 351 { 352 /* do nothing, just avoid next clauses */ 353 } 354 else if (c == '(') 355 { 356 cmntcnt++; 357 c = NOCHAR; 358 } 359 else if (c == ')') 360 { 361 if (cmntcnt <= 0) 362 { 363 usrerr("Unbalanced ')'"); 364 DelimChar = p; 365 return (NULL); 366 } 367 else 368 cmntcnt--; 369 } 370 else if (cmntcnt > 0) 371 c = NOCHAR; 372 else if (c == '<') 373 anglecnt++; 374 else if (c == '>') 375 { 376 if (anglecnt <= 0) 377 { 378 usrerr("Unbalanced '>'"); 379 DelimChar = p; 380 return (NULL); 381 } 382 anglecnt--; 383 } 384 else if (delim == ' ' && isspace(c)) 385 c = ' '; 386 387 if (c == NOCHAR) 388 continue; 389 390 /* see if this is end of input */ 391 if (c == delim && anglecnt <= 0 && state != QST) 392 break; 393 394 newstate = StateTab[state][toktype(c)]; 395 if (tTd(22, 101)) 396 printf("ns=%02o\n", newstate); 397 state = newstate & TYPE; 398 if (bitset(M, newstate)) 399 c = NOCHAR; 400 if (bitset(B, newstate)) 401 break; 402 } 403 404 /* new token */ 405 if (tok != q) 406 { 407 *q++ = '\0'; 408 if (tTd(22, 36)) 409 { 410 printf("tok="); 411 xputs(tok); 412 (void) putchar('\n'); 413 } 414 if (avp >= &av[MAXATOM]) 415 { 416 syserr("prescan: too many tokens"); 417 DelimChar = p; 418 return (NULL); 419 } 420 *avp++ = tok; 421 } 422 } while (c != '\0' && (c != delim || anglecnt > 0)); 423 *avp = NULL; 424 DelimChar = --p; 425 if (cmntcnt > 0) 426 usrerr("Unbalanced '('"); 427 else if (anglecnt > 0) 428 usrerr("Unbalanced '<'"); 429 else if (state == QST) 430 usrerr("Unbalanced '\"'"); 431 else if (av[0] != NULL) 432 return (av); 433 return (NULL); 434 } 435 /* 436 ** TOKTYPE -- return token type 437 ** 438 ** Parameters: 439 ** c -- the character in question. 440 ** 441 ** Returns: 442 ** Its type. 443 ** 444 ** Side Effects: 445 ** none. 446 */ 447 448 toktype(c) 449 register char c; 450 { 451 static char buf[50]; 452 static bool firstime = TRUE; 453 454 if (firstime) 455 { 456 firstime = FALSE; 457 expand("\001o", buf, &buf[sizeof buf - 1], CurEnv); 458 (void) strcat(buf, DELIMCHARS); 459 } 460 if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS) 461 return (ONE); 462 if (c == '"') 463 return (QST); 464 if (!isascii(c)) 465 return (ATM); 466 if (isspace(c) || c == ')') 467 return (SPC); 468 if (iscntrl(c) || index(buf, c) != NULL) 469 return (OPR); 470 return (ATM); 471 } 472 /* 473 ** REWRITE -- apply rewrite rules to token vector. 474 ** 475 ** This routine is an ordered production system. Each rewrite 476 ** rule has a LHS (called the pattern) and a RHS (called the 477 ** rewrite); 'rwr' points the the current rewrite rule. 478 ** 479 ** For each rewrite rule, 'avp' points the address vector we 480 ** are trying to match against, and 'pvp' points to the pattern. 481 ** If pvp points to a special match value (MATCHZANY, MATCHANY, 482 ** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp 483 ** matched is saved away in the match vector (pointed to by 'mvp'). 484 ** 485 ** When a match between avp & pvp does not match, we try to 486 ** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS 487 ** we must also back out the match in mvp. If we reach a 488 ** MATCHANY or MATCHZANY we just extend the match and start 489 ** over again. 490 ** 491 ** When we finally match, we rewrite the address vector 492 ** and try over again. 493 ** 494 ** Parameters: 495 ** pvp -- pointer to token vector. 496 ** 497 ** Returns: 498 ** none. 499 ** 500 ** Side Effects: 501 ** pvp is modified. 502 */ 503 504 struct match 505 { 506 char **first; /* first token matched */ 507 char **last; /* last token matched */ 508 }; 509 510 # define MAXMATCH 9 /* max params per rewrite */ 511 512 513 rewrite(pvp, ruleset) 514 char **pvp; 515 int ruleset; 516 { 517 register char *ap; /* address pointer */ 518 register char *rp; /* rewrite pointer */ 519 register char **avp; /* address vector pointer */ 520 register char **rvp; /* rewrite vector pointer */ 521 register struct match *mlp; /* cur ptr into mlist */ 522 register struct rewrite *rwr; /* pointer to current rewrite rule */ 523 struct match mlist[MAXMATCH]; /* stores match on LHS */ 524 char *npvp[MAXATOM+1]; /* temporary space for rebuild */ 525 526 if (OpMode == MD_TEST || tTd(21, 2)) 527 { 528 printf("rewrite: ruleset %2d input:", ruleset); 529 printav(pvp); 530 } 531 if (pvp == NULL) 532 return; 533 534 /* 535 ** Run through the list of rewrite rules, applying 536 ** any that match. 537 */ 538 539 for (rwr = RewriteRules[ruleset]; rwr != NULL; ) 540 { 541 int loopcount = 0; 542 543 if (tTd(21, 12)) 544 { 545 printf("-----trying rule:"); 546 printav(rwr->r_lhs); 547 } 548 549 /* try to match on this rule */ 550 mlp = mlist; 551 rvp = rwr->r_lhs; 552 avp = pvp; 553 while ((ap = *avp) != NULL || *rvp != NULL) 554 { 555 if (++loopcount > 100) 556 { 557 syserr("Infinite loop in ruleset %d", ruleset); 558 break; 559 } 560 rp = *rvp; 561 if (tTd(21, 35)) 562 { 563 printf("operator="); 564 xputs(ap); 565 printf(", token="); 566 xputs(rp); 567 printf("\n"); 568 } 569 if (rp == NULL) 570 { 571 /* end-of-pattern before end-of-address */ 572 goto backup; 573 } 574 if (ap == NULL && *rp != MATCHZANY) 575 { 576 /* end-of-input */ 577 break; 578 } 579 580 switch (*rp) 581 { 582 register STAB *s; 583 584 case MATCHCLASS: 585 case MATCHNCLASS: 586 /* match any token in (not in) a class */ 587 s = stab(ap, ST_CLASS, ST_FIND); 588 if (s == NULL || !bitnset(rp[1], s->s_class)) 589 { 590 if (*rp == MATCHCLASS) 591 goto backup; 592 } 593 else if (*rp == MATCHNCLASS) 594 goto backup; 595 596 /* explicit fall-through */ 597 598 case MATCHONE: 599 case MATCHANY: 600 /* match exactly one token */ 601 mlp->first = avp; 602 mlp->last = avp++; 603 mlp++; 604 break; 605 606 case MATCHZANY: 607 /* match zero or more tokens */ 608 mlp->first = avp; 609 mlp->last = avp - 1; 610 mlp++; 611 break; 612 613 default: 614 /* must have exact match */ 615 if (strcasecmp(rp, ap)) 616 goto backup; 617 avp++; 618 break; 619 } 620 621 /* successful match on this token */ 622 rvp++; 623 continue; 624 625 backup: 626 /* match failed -- back up */ 627 while (--rvp >= rwr->r_lhs) 628 { 629 rp = *rvp; 630 if (*rp == MATCHANY || *rp == MATCHZANY) 631 { 632 /* extend binding and continue */ 633 avp = ++mlp[-1].last; 634 avp++; 635 rvp++; 636 break; 637 } 638 avp--; 639 if (*rp == MATCHONE || *rp == MATCHCLASS || 640 *rp == MATCHNCLASS) 641 { 642 /* back out binding */ 643 mlp--; 644 } 645 } 646 647 if (rvp < rwr->r_lhs) 648 { 649 /* total failure to match */ 650 break; 651 } 652 } 653 654 /* 655 ** See if we successfully matched 656 */ 657 658 if (rvp < rwr->r_lhs || *rvp != NULL) 659 { 660 if (tTd(21, 10)) 661 printf("----- rule fails\n"); 662 rwr = rwr->r_next; 663 continue; 664 } 665 666 rvp = rwr->r_rhs; 667 if (tTd(21, 12)) 668 { 669 printf("-----rule matches:"); 670 printav(rvp); 671 } 672 673 rp = *rvp; 674 if (*rp == CANONUSER) 675 { 676 rvp++; 677 rwr = rwr->r_next; 678 } 679 else if (*rp == CANONHOST) 680 { 681 rvp++; 682 rwr = NULL; 683 } 684 else if (*rp == CANONNET) 685 rwr = NULL; 686 687 /* substitute */ 688 for (avp = npvp; *rvp != NULL; rvp++) 689 { 690 register struct match *m; 691 register char **pp; 692 693 rp = *rvp; 694 if (*rp == MATCHREPL) 695 { 696 /* substitute from LHS */ 697 m = &mlist[rp[1] - '1']; 698 if (m >= mlp) 699 { 700 syserr("rewrite: ruleset %d: replacement out of bounds", ruleset); 701 return; 702 } 703 if (tTd(21, 15)) 704 { 705 printf("$%c:", rp[1]); 706 pp = m->first; 707 while (pp <= m->last) 708 { 709 printf(" %x=\"", *pp); 710 (void) fflush(stdout); 711 printf("%s\"", *pp++); 712 } 713 printf("\n"); 714 } 715 pp = m->first; 716 while (pp <= m->last) 717 { 718 if (avp >= &npvp[MAXATOM]) 719 { 720 syserr("rewrite: expansion too long"); 721 return; 722 } 723 *avp++ = *pp++; 724 } 725 } 726 else 727 { 728 /* vanilla replacement */ 729 if (avp >= &npvp[MAXATOM]) 730 { 731 toolong: 732 syserr("rewrite: expansion too long"); 733 return; 734 } 735 *avp++ = rp; 736 } 737 } 738 *avp++ = NULL; 739 740 /* 741 ** Check for any hostname/keyword lookups. 742 */ 743 744 for (rvp = npvp; *rvp != NULL; rvp++) 745 { 746 char **hbrvp; 747 char **xpvp; 748 int trsize; 749 char *olddelimchar; 750 char *replac; 751 int endtoken; 752 STAB *map; 753 char *mapname; 754 char **key_rvp; 755 char **arg_rvp; 756 char **default_rvp; 757 char buf[MAXNAME + 1]; 758 char *pvpb1[MAXATOM + 1]; 759 char pvpbuf[PSBUFSIZE]; 760 extern char *DelimChar; 761 762 if (**rvp != HOSTBEGIN && **rvp != LOOKUPBEGIN) 763 continue; 764 765 /* 766 ** Got a hostname/keyword lookup. 767 ** 768 ** This could be optimized fairly easily. 769 */ 770 771 hbrvp = rvp; 772 arg_rvp = default_rvp = NULL; 773 if (**rvp == HOSTBEGIN) 774 { 775 endtoken = HOSTEND; 776 mapname = "host"; 777 } 778 else 779 { 780 endtoken = LOOKUPEND; 781 mapname = *++rvp; 782 } 783 map = stab(mapname, ST_MAP, ST_FIND); 784 if (map == NULL) 785 syserr("rewrite: map %s not found", mapname); 786 787 /* extract the match part */ 788 key_rvp = ++rvp; 789 while (*rvp != NULL && **rvp != endtoken) 790 { 791 switch (**rvp) 792 { 793 case CANONHOST: 794 *rvp++ = NULL; 795 arg_rvp = rvp; 796 break; 797 798 case CANONUSER: 799 *rvp++ = NULL; 800 default_rvp = rvp; 801 break; 802 803 default: 804 rvp++; 805 break; 806 } 807 } 808 if (*rvp != NULL) 809 *rvp++ = NULL; 810 811 /* save the remainder of the input string */ 812 trsize = (int) (avp - rvp + 1) * sizeof *rvp; 813 bcopy((char *) rvp, (char *) pvpb1, trsize); 814 815 /* look it up */ 816 cataddr(key_rvp, buf, sizeof buf); 817 if (map != NULL && bitset(MF_VALID, map->s_map.map_flags)) 818 replac = (*map->s_map.map_class->map_lookup)(buf, 819 sizeof buf - 1, arg_rvp); 820 else 821 replac = NULL; 822 823 /* if no replacement, use default */ 824 if (replac == NULL) 825 { 826 if (default_rvp != NULL) 827 xpvp = default_rvp; 828 else 829 xpvp = key_rvp; 830 } 831 else 832 { 833 /* scan the new replacement */ 834 olddelimchar = DelimChar; 835 xpvp = prescan(replac, '\0', pvpbuf); 836 DelimChar = olddelimchar; 837 if (xpvp == NULL) 838 { 839 syserr("rewrite: cannot prescan map value: %s", replac); 840 return; 841 } 842 } 843 844 /* append it to the token list */ 845 for (avp = hbrvp; *xpvp != NULL; xpvp++) 846 { 847 *avp++ = newstr(*xpvp); 848 if (avp >= &npvp[MAXATOM]) 849 goto toolong; 850 } 851 852 /* restore the old trailing information */ 853 for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) 854 if (avp >= &npvp[MAXATOM]) 855 goto toolong; 856 857 break; 858 } 859 860 /* 861 ** Check for subroutine calls. 862 */ 863 864 if (*npvp != NULL && **npvp == CALLSUBR) 865 { 866 bcopy((char *) &npvp[2], (char *) pvp, 867 (int) (avp - npvp - 2) * sizeof *avp); 868 if (tTd(21, 3)) 869 printf("-----callsubr %s\n", npvp[1]); 870 rewrite(pvp, atoi(npvp[1])); 871 } 872 else 873 { 874 bcopy((char *) npvp, (char *) pvp, 875 (int) (avp - npvp) * sizeof *avp); 876 } 877 if (tTd(21, 4)) 878 { 879 printf("rewritten as:"); 880 printav(pvp); 881 } 882 } 883 884 if (OpMode == MD_TEST || tTd(21, 2)) 885 { 886 printf("rewrite: ruleset %2d returns:", ruleset); 887 printav(pvp); 888 } 889 } 890 /* 891 ** BUILDADDR -- build address from token vector. 892 ** 893 ** Parameters: 894 ** tv -- token vector. 895 ** a -- pointer to address descriptor to fill. 896 ** If NULL, one will be allocated. 897 ** 898 ** Returns: 899 ** NULL if there was an error. 900 ** 'a' otherwise. 901 ** 902 ** Side Effects: 903 ** fills in 'a' 904 */ 905 906 ADDRESS * 907 buildaddr(tv, a) 908 register char **tv; 909 register ADDRESS *a; 910 { 911 static char buf[MAXNAME]; 912 struct mailer **mp; 913 register struct mailer *m; 914 915 if (a == NULL) 916 a = (ADDRESS *) xalloc(sizeof *a); 917 bzero((char *) a, sizeof *a); 918 919 /* figure out what net/mailer to use */ 920 if (**tv != CANONNET) 921 { 922 syserr("buildaddr: no net"); 923 return (NULL); 924 } 925 tv++; 926 if (!strcasecmp(*tv, "error")) 927 { 928 if (**++tv == CANONHOST) 929 { 930 setstat(atoi(*++tv)); 931 tv++; 932 } 933 if (**tv != CANONUSER) 934 syserr("buildaddr: error: no user"); 935 buf[0] = '\0'; 936 while (*++tv != NULL) 937 { 938 if (buf[0] != '\0') 939 (void) strcat(buf, " "); 940 (void) strcat(buf, *tv); 941 } 942 usrerr(buf); 943 return (NULL); 944 } 945 for (mp = Mailer; (m = *mp++) != NULL; ) 946 { 947 if (!strcasecmp(m->m_name, *tv)) 948 break; 949 } 950 if (m == NULL) 951 { 952 syserr("buildaddr: unknown mailer %s", *tv); 953 return (NULL); 954 } 955 a->q_mailer = m; 956 957 /* figure out what host (if any) */ 958 tv++; 959 if (!bitnset(M_LOCAL, m->m_flags)) 960 { 961 if (**tv++ != CANONHOST) 962 { 963 syserr("buildaddr: no host"); 964 return (NULL); 965 } 966 buf[0] = '\0'; 967 while (*tv != NULL && **tv != CANONUSER) 968 (void) strcat(buf, *tv++); 969 a->q_host = newstr(buf); 970 } 971 else 972 a->q_host = NULL; 973 974 /* figure out the user */ 975 if (*tv == NULL || **tv != CANONUSER) 976 { 977 syserr("buildaddr: no user"); 978 return (NULL); 979 } 980 981 if (m == LocalMailer && tv[1] != NULL && strcmp(tv[1], "@") == 0) 982 { 983 tv++; 984 a->q_flags |= QNOTREMOTE; 985 } 986 987 /* rewrite according recipient mailer rewriting rules */ 988 rewrite(++tv, 2); 989 if (m->m_r_rwset > 0) 990 rewrite(tv, m->m_r_rwset); 991 rewrite(tv, 4); 992 993 /* save the result for the command line/RCPT argument */ 994 cataddr(tv, buf, sizeof buf); 995 a->q_user = buf; 996 997 return (a); 998 } 999 /* 1000 ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 1001 ** 1002 ** Parameters: 1003 ** pvp -- parameter vector to rebuild. 1004 ** buf -- buffer to build the string into. 1005 ** sz -- size of buf. 1006 ** 1007 ** Returns: 1008 ** none. 1009 ** 1010 ** Side Effects: 1011 ** Destroys buf. 1012 */ 1013 1014 cataddr(pvp, buf, sz) 1015 char **pvp; 1016 char *buf; 1017 register int sz; 1018 { 1019 bool oatomtok = FALSE; 1020 bool natomtok = FALSE; 1021 register int i; 1022 register char *p; 1023 1024 if (pvp == NULL) 1025 { 1026 (void) strcpy(buf, ""); 1027 return; 1028 } 1029 p = buf; 1030 sz -= 2; 1031 while (*pvp != NULL && (i = strlen(*pvp)) < sz) 1032 { 1033 natomtok = (toktype(**pvp) == ATM); 1034 if (oatomtok && natomtok) 1035 *p++ = SpaceSub; 1036 (void) strcpy(p, *pvp); 1037 oatomtok = natomtok; 1038 p += i; 1039 sz -= i + 1; 1040 pvp++; 1041 } 1042 *p = '\0'; 1043 } 1044 /* 1045 ** SAMEADDR -- Determine if two addresses are the same 1046 ** 1047 ** This is not just a straight comparison -- if the mailer doesn't 1048 ** care about the host we just ignore it, etc. 1049 ** 1050 ** Parameters: 1051 ** a, b -- pointers to the internal forms to compare. 1052 ** 1053 ** Returns: 1054 ** TRUE -- they represent the same mailbox. 1055 ** FALSE -- they don't. 1056 ** 1057 ** Side Effects: 1058 ** none. 1059 */ 1060 1061 bool 1062 sameaddr(a, b) 1063 register ADDRESS *a; 1064 register ADDRESS *b; 1065 { 1066 /* if they don't have the same mailer, forget it */ 1067 if (a->q_mailer != b->q_mailer) 1068 return (FALSE); 1069 1070 /* if the user isn't the same, we can drop out */ 1071 if (strcmp(a->q_user, b->q_user) != 0) 1072 return (FALSE); 1073 1074 /* if the mailer ignores hosts, we have succeeded! */ 1075 if (bitnset(M_LOCAL, a->q_mailer->m_flags)) 1076 return (TRUE); 1077 1078 /* otherwise compare hosts (but be careful for NULL ptrs) */ 1079 if (a->q_host == NULL || b->q_host == NULL) 1080 return (FALSE); 1081 if (strcmp(a->q_host, b->q_host) != 0) 1082 return (FALSE); 1083 1084 return (TRUE); 1085 } 1086 /* 1087 ** PRINTADDR -- print address (for debugging) 1088 ** 1089 ** Parameters: 1090 ** a -- the address to print 1091 ** follow -- follow the q_next chain. 1092 ** 1093 ** Returns: 1094 ** none. 1095 ** 1096 ** Side Effects: 1097 ** none. 1098 */ 1099 1100 printaddr(a, follow) 1101 register ADDRESS *a; 1102 bool follow; 1103 { 1104 bool first = TRUE; 1105 1106 while (a != NULL) 1107 { 1108 first = FALSE; 1109 printf("%x=", a); 1110 (void) fflush(stdout); 1111 printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n", 1112 a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name, 1113 a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>"); 1114 printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags, 1115 a->q_alias); 1116 printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home, 1117 a->q_fullname); 1118 1119 if (!follow) 1120 return; 1121 a = a->q_next; 1122 } 1123 if (first) 1124 printf("[NULL]\n"); 1125 } 1126 1127 /* 1128 ** REMOTENAME -- return the name relative to the current mailer 1129 ** 1130 ** Parameters: 1131 ** name -- the name to translate. 1132 ** m -- the mailer that we want to do rewriting relative 1133 ** to. 1134 ** senderaddress -- if set, uses the sender rewriting rules 1135 ** rather than the recipient rewriting rules. 1136 ** canonical -- if set, strip out any comment information, 1137 ** etc. 1138 ** 1139 ** Returns: 1140 ** the text string representing this address relative to 1141 ** the receiving mailer. 1142 ** 1143 ** Side Effects: 1144 ** none. 1145 ** 1146 ** Warnings: 1147 ** The text string returned is tucked away locally; 1148 ** copy it if you intend to save it. 1149 */ 1150 1151 char * 1152 remotename(name, m, senderaddress, canonical) 1153 char *name; 1154 struct mailer *m; 1155 bool senderaddress; 1156 bool canonical; 1157 { 1158 register char **pvp; 1159 char *fancy; 1160 extern char *macvalue(); 1161 char *oldg = macvalue('g', CurEnv); 1162 static char buf[MAXNAME]; 1163 char lbuf[MAXNAME]; 1164 char pvpbuf[PSBUFSIZE]; 1165 extern char **prescan(); 1166 extern char *crackaddr(); 1167 1168 if (tTd(12, 1)) 1169 printf("remotename(%s)\n", name); 1170 1171 /* don't do anything if we are tagging it as special */ 1172 if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0) 1173 return (name); 1174 1175 /* 1176 ** Do a heuristic crack of this name to extract any comment info. 1177 ** This will leave the name as a comment and a $g macro. 1178 */ 1179 1180 if (canonical) 1181 fancy = "\001g"; 1182 else 1183 fancy = crackaddr(name); 1184 1185 /* 1186 ** Turn the name into canonical form. 1187 ** Normally this will be RFC 822 style, i.e., "user@domain". 1188 ** If this only resolves to "user", and the "C" flag is 1189 ** specified in the sending mailer, then the sender's 1190 ** domain will be appended. 1191 */ 1192 1193 pvp = prescan(name, '\0', pvpbuf); 1194 if (pvp == NULL) 1195 return (name); 1196 rewrite(pvp, 3); 1197 if (CurEnv->e_fromdomain != NULL) 1198 { 1199 /* append from domain to this address */ 1200 register char **pxp = pvp; 1201 1202 /* see if there is an "@domain" in the current name */ 1203 while (*pxp != NULL && strcmp(*pxp, "@") != 0) 1204 pxp++; 1205 if (*pxp == NULL) 1206 { 1207 /* no.... append the "@domain" from the sender */ 1208 register char **qxq = CurEnv->e_fromdomain; 1209 1210 while ((*pxp++ = *qxq++) != NULL) 1211 continue; 1212 rewrite(pvp, 3); 1213 } 1214 } 1215 1216 /* 1217 ** Do more specific rewriting. 1218 ** Rewrite using ruleset 1 or 2 depending on whether this is 1219 ** a sender address or not. 1220 ** Then run it through any receiving-mailer-specific rulesets. 1221 */ 1222 1223 if (senderaddress) 1224 { 1225 rewrite(pvp, 1); 1226 if (m->m_s_rwset > 0) 1227 rewrite(pvp, m->m_s_rwset); 1228 } 1229 else 1230 { 1231 rewrite(pvp, 2); 1232 if (m->m_r_rwset > 0) 1233 rewrite(pvp, m->m_r_rwset); 1234 } 1235 1236 /* 1237 ** Do any final sanitation the address may require. 1238 ** This will normally be used to turn internal forms 1239 ** (e.g., user@host.LOCAL) into external form. This 1240 ** may be used as a default to the above rules. 1241 */ 1242 1243 rewrite(pvp, 4); 1244 1245 /* 1246 ** Now restore the comment information we had at the beginning. 1247 */ 1248 1249 cataddr(pvp, lbuf, sizeof lbuf); 1250 define('g', lbuf, CurEnv); 1251 expand(fancy, buf, &buf[sizeof buf - 1], CurEnv); 1252 define('g', oldg, CurEnv); 1253 1254 if (tTd(12, 1)) 1255 printf("remotename => `%s'\n", buf); 1256 return (buf); 1257 } 1258 /* 1259 ** MAPLOCALUSER -- run local username through ruleset 5 for final redirection 1260 ** 1261 ** Parameters: 1262 ** a -- the address to map (but just the user name part). 1263 ** sendq -- the sendq in which to install any replacement 1264 ** addresses. 1265 ** 1266 ** Returns: 1267 ** none. 1268 */ 1269 1270 maplocaluser(a, sendq) 1271 register ADDRESS *a; 1272 ADDRESS **sendq; 1273 { 1274 register char **pvp; 1275 register ADDRESS *a1 = NULL; 1276 char pvpbuf[PSBUFSIZE]; 1277 1278 if (tTd(29, 1)) 1279 { 1280 printf("maplocaluser: "); 1281 printaddr(a, FALSE); 1282 } 1283 pvp = prescan(a->q_user, '\0', pvpbuf); 1284 if (pvp == NULL) 1285 return; 1286 1287 rewrite(pvp, 5); 1288 if (pvp[0] == NULL || pvp[0][0] != CANONNET) 1289 return; 1290 1291 /* if non-null, mailer destination specified -- has it changed? */ 1292 a1 = buildaddr(pvp, NULL); 1293 if (a1 == NULL || sameaddr(a, a1)) 1294 return; 1295 1296 /* mark old address as dead; insert new address */ 1297 a->q_flags |= QDONTSEND; 1298 a1->q_alias = a; 1299 allocaddr(a1, 1, NULL); 1300 (void) recipient(a1, sendq); 1301 } 1302