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