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