1 # include "sendmail.h" 2 3 static char SccsId[] = "@(#)parseaddr.c 3.25 09/14/81"; 4 5 /* 6 ** PARSE -- Parse an address 7 ** 8 ** Parses an address and breaks it up into three parts: a 9 ** net to transmit the message on, the host to transmit it 10 ** to, and a user on that host. These are loaded into an 11 ** ADDRESS header with the values squirreled away if necessary. 12 ** The "user" part may not be a real user; the process may 13 ** just reoccur on that machine. For example, on a machine 14 ** with an arpanet connection, the address 15 ** csvax.bill@berkeley 16 ** will break up to a "user" of 'csvax.bill' and a host 17 ** of 'berkeley' -- to be transmitted over the arpanet. 18 ** 19 ** Parameters: 20 ** addr -- the address to parse. 21 ** a -- a pointer to the address descriptor buffer. 22 ** If NULL, a header will be created. 23 ** copyf -- determines what shall be copied: 24 ** -1 -- don't copy anything. The printname 25 ** (q_paddr) is just addr, and the 26 ** user & host are allocated internally 27 ** to parse. 28 ** 0 -- copy out the parsed user & host, but 29 ** don't copy the printname. 30 ** +1 -- copy everything. 31 ** 32 ** Returns: 33 ** A pointer to the address descriptor header (`a' if 34 ** `a' is non-NULL). 35 ** NULL on error. 36 ** 37 ** Side Effects: 38 ** none 39 */ 40 41 # define DELIMCHARS "$()<>,;\\\"\r\n" /* word delimiters */ 42 43 ADDRESS * 44 parse(addr, a, copyf) 45 char *addr; 46 register ADDRESS *a; 47 int copyf; 48 { 49 register char **pvp; 50 register struct mailer *m; 51 extern char **prescan(); 52 extern ADDRESS *buildaddr(); 53 54 /* 55 ** Initialize and prescan address. 56 */ 57 58 To = addr; 59 # ifdef DEBUG 60 if (Debug) 61 printf("\n--parse(%s)\n", addr); 62 # endif DEBUG 63 64 pvp = prescan(addr, '\0'); 65 if (pvp == NULL) 66 return (NULL); 67 68 /* 69 ** Apply rewriting rules. 70 */ 71 72 rewrite(pvp, 0); 73 74 /* 75 ** See if we resolved to a real mailer. 76 */ 77 78 if (pvp[0][0] != CANONNET) 79 { 80 setstat(EX_USAGE); 81 usrerr("cannot resolve name"); 82 return (NULL); 83 } 84 85 /* 86 ** Build canonical address from pvp. 87 */ 88 89 a = buildaddr(pvp, a); 90 if (a == NULL) 91 return (NULL); 92 m = Mailer[a->q_mailer]; 93 94 /* 95 ** Make local copies of the host & user and then 96 ** transport them out. 97 */ 98 99 if (copyf > 0) 100 a->q_paddr = newstr(addr); 101 else 102 a->q_paddr = addr; 103 104 if (copyf >= 0) 105 { 106 if (a->q_host != NULL) 107 a->q_host = newstr(a->q_host); 108 else 109 a->q_host = ""; 110 if (a->q_user != a->q_paddr) 111 a->q_user = newstr(a->q_user); 112 } 113 114 /* 115 ** Do UPPER->lower case mapping unless inhibited. 116 */ 117 118 if (!bitset(M_HST_UPPER, m->m_flags)) 119 makelower(a->q_host); 120 if (!bitset(M_USR_UPPER, m->m_flags)) 121 makelower(a->q_user); 122 123 /* 124 ** Compute return value. 125 */ 126 127 # ifdef DEBUG 128 if (Debug) 129 printf("parse(\"%s\"): host \"%s\" user \"%s\" mailer %d\n", 130 addr, a->q_host, a->q_user, a->q_mailer); 131 # endif DEBUG 132 133 return (a); 134 } 135 /* 136 ** PRESCAN -- Prescan name and make it canonical 137 ** 138 ** Scans a name and turns it into canonical form. This involves 139 ** deleting blanks, comments (in parentheses), and turning the 140 ** word "at" into an at-sign ("@"). The name is copied as this 141 ** is done; it is legal to copy a name onto itself, since this 142 ** process can only make things smaller. 143 ** 144 ** This routine knows about quoted strings and angle brackets. 145 ** 146 ** There are certain subtleties to this routine. The one that 147 ** comes to mind now is that backslashes on the ends of names 148 ** are silently stripped off; this is intentional. The problem 149 ** is that some versions of sndmsg (like at LBL) set the kill 150 ** character to something other than @ when reading addresses; 151 ** so people type "csvax.eric\@berkeley" -- which screws up the 152 ** berknet mailer. 153 ** 154 ** Parameters: 155 ** addr -- the name to chomp. 156 ** delim -- the delimiter for the address, normally 157 ** '\0' or ','; \0 is accepted in any case. 158 ** are moving in place; set buflim to high core. 159 ** 160 ** Returns: 161 ** A pointer to a vector of tokens. 162 ** NULL on error. 163 ** 164 ** Side Effects: 165 ** none. 166 */ 167 168 # define OPER 1 169 # define ATOM 2 170 # define EOTOK 3 171 # define QSTRING 4 172 # define SPACE 5 173 # define DOLLAR 6 174 # define GETONE 7 175 176 char ** 177 prescan(addr, delim) 178 char *addr; 179 char delim; 180 { 181 register char *p; 182 static char buf[MAXNAME+MAXATOM]; 183 static char *av[MAXATOM+1]; 184 char **avp; 185 bool bslashmode; 186 int cmntcnt; 187 int brccnt; 188 register char c; 189 char *tok; 190 register char *q; 191 register int state; 192 int nstate; 193 extern char lower(); 194 195 q = buf; 196 bslashmode = FALSE; 197 cmntcnt = brccnt = 0; 198 avp = av; 199 state = OPER; 200 for (p = addr; *p != '\0' && *p != delim; ) 201 { 202 /* read a token */ 203 tok = q; 204 while ((c = *p++) != '\0' && c != delim) 205 { 206 /* chew up special characters */ 207 c &= ~0200; 208 *q = '\0'; 209 if (bslashmode) 210 { 211 c |= 0200; 212 bslashmode = FALSE; 213 } 214 else if (c == '\\') 215 { 216 bslashmode = TRUE; 217 continue; 218 } 219 else if (c == '"') 220 { 221 if (state == QSTRING) 222 state = OPER; 223 else 224 state = QSTRING; 225 break; 226 } 227 228 nstate = toktype(c); 229 switch (state) 230 { 231 case QSTRING: /* in quoted string */ 232 break; 233 234 case ATOM: /* regular atom */ 235 /* state = nstate; */ 236 if (nstate != ATOM) 237 { 238 state = EOTOK; 239 p--; 240 } 241 break; 242 243 case GETONE: /* grab one character */ 244 state = OPER; 245 break; 246 247 case EOTOK: /* after atom or q-string */ 248 state = nstate; 249 if (state == SPACE) 250 continue; 251 break; 252 253 case SPACE: /* linear white space */ 254 state = nstate; 255 break; 256 257 case OPER: /* operator */ 258 if (nstate == SPACE) 259 continue; 260 state = nstate; 261 break; 262 263 case DOLLAR: /* $- etc. */ 264 state = OPER; 265 switch (c) 266 { 267 case '$': /* literal $ */ 268 break; 269 270 case '+': /* match anything */ 271 c = MATCHANY; 272 state = GETONE; 273 break; 274 275 case '-': /* match one token */ 276 c = MATCHONE; 277 state = GETONE; 278 break; 279 280 case '=': /* match one token of class */ 281 c = MATCHCLASS; 282 state = GETONE; 283 break; 284 285 case '#': /* canonical net name */ 286 c = CANONNET; 287 break; 288 289 case '@': /* canonical host name */ 290 c = CANONHOST; 291 break; 292 293 case ':': /* canonical user name */ 294 c = CANONUSER; 295 break; 296 297 default: 298 c = '$'; 299 state = OPER; 300 p--; 301 break; 302 } 303 break; 304 305 default: 306 syserr("prescan: unknown state %d", state); 307 } 308 309 if (state == EOTOK || state == SPACE) 310 break; 311 if (c == '$' && delim == '\t') 312 { 313 state = DOLLAR; 314 continue; 315 } 316 317 /* squirrel it away */ 318 if (q >= &buf[sizeof buf - 5]) 319 { 320 usrerr("Address too long"); 321 return (NULL); 322 } 323 *q++ = c; 324 325 /* decide whether this represents end of token */ 326 if (state == OPER) 327 break; 328 } 329 if (c == '\0' || c == delim) 330 p--; 331 332 /* new token */ 333 if (tok == q) 334 continue; 335 *q++ = '\0'; 336 337 c = tok[0]; 338 if (c == '(') 339 { 340 cmntcnt++; 341 continue; 342 } 343 else if (c == ')') 344 { 345 if (cmntcnt <= 0) 346 { 347 usrerr("Unbalanced ')'"); 348 return (NULL); 349 } 350 else 351 { 352 cmntcnt--; 353 continue; 354 } 355 } 356 else if (cmntcnt > 0) 357 continue; 358 359 if (avp >= &av[MAXATOM]) 360 { 361 syserr("prescan: too many tokens"); 362 return (NULL); 363 } 364 *avp++ = tok; 365 366 /* we prefer <> specs */ 367 if (c == '<') 368 { 369 if (brccnt < 0) 370 { 371 usrerr("multiple < spec"); 372 return (NULL); 373 } 374 brccnt++; 375 if (brccnt == 1) 376 { 377 /* we prefer using machine readable name */ 378 q = buf; 379 *q = '\0'; 380 avp = av; 381 continue; 382 } 383 } 384 else if (c == '>') 385 { 386 if (brccnt <= 0) 387 { 388 usrerr("Unbalanced `>'"); 389 return (NULL); 390 } 391 else 392 brccnt--; 393 if (brccnt <= 0) 394 { 395 brccnt = -1; 396 continue; 397 } 398 } 399 } 400 *avp = NULL; 401 if (cmntcnt > 0) 402 usrerr("Unbalanced '('"); 403 else if (brccnt > 0) 404 usrerr("Unbalanced '<'"); 405 else if (state == QSTRING) 406 usrerr("Unbalanced '\"'"); 407 else if (av[0] != NULL) 408 return (av); 409 return (NULL); 410 } 411 /* 412 ** TOKTYPE -- return token type 413 ** 414 ** Parameters: 415 ** c -- the character in question. 416 ** 417 ** Returns: 418 ** Its type. 419 ** 420 ** Side Effects: 421 ** none. 422 */ 423 424 toktype(c) 425 register char c; 426 { 427 static char buf[50]; 428 static bool firstime = TRUE; 429 430 if (firstime) 431 { 432 firstime = FALSE; 433 (void) expand("$o", buf, &buf[sizeof buf - 1]); 434 strcat(buf, DELIMCHARS); 435 } 436 if (!isascii(c)) 437 return (ATOM); 438 if (isspace(c)) 439 return (SPACE); 440 if (iscntrl(c) || index(buf, c) != NULL) 441 return (OPER); 442 return (ATOM); 443 } 444 /* 445 ** REWRITE -- apply rewrite rules to token vector. 446 ** 447 ** Parameters: 448 ** pvp -- pointer to token vector. 449 ** 450 ** Returns: 451 ** none. 452 ** 453 ** Side Effects: 454 ** pvp is modified. 455 */ 456 457 struct match 458 { 459 char **firsttok; /* first token matched */ 460 char **lasttok; /* last token matched */ 461 char name; /* name of parameter */ 462 }; 463 464 # define MAXMATCH 8 /* max params per rewrite */ 465 466 467 rewrite(pvp, ruleset) 468 char **pvp; 469 int ruleset; 470 { 471 register char *ap; /* address pointer */ 472 register char *rp; /* rewrite pointer */ 473 register char **avp; /* address vector pointer */ 474 register char **rvp; /* rewrite vector pointer */ 475 struct rewrite *rwr; 476 struct match mlist[MAXMATCH]; 477 char *npvp[MAXATOM+1]; /* temporary space for rebuild */ 478 extern bool sameword(); 479 480 # ifdef DEBUG 481 if (Debug > 9) 482 { 483 printf("rewrite: original pvp:\n"); 484 printav(pvp); 485 } 486 # endif DEBUG 487 488 /* 489 ** Run through the list of rewrite rules, applying 490 ** any that match. 491 */ 492 493 for (rwr = RewriteRules[ruleset]; rwr != NULL; ) 494 { 495 # ifdef DEBUG 496 if (Debug > 10) 497 { 498 printf("-----trying rule:\n"); 499 printav(rwr->r_lhs); 500 } 501 # endif DEBUG 502 503 /* try to match on this rule */ 504 clrmatch(mlist); 505 for (rvp = rwr->r_lhs, avp = pvp; *avp != NULL; ) 506 { 507 ap = *avp; 508 rp = *rvp; 509 510 if (rp == NULL) 511 { 512 /* end-of-pattern before end-of-address */ 513 goto fail; 514 } 515 516 switch (*rp) 517 { 518 register STAB *s; 519 register int class; 520 521 case MATCHONE: 522 /* match exactly one token */ 523 setmatch(mlist, rp[1], avp, avp); 524 break; 525 526 case MATCHANY: 527 /* match any number of tokens */ 528 setmatch(mlist, rp[1], (char **) NULL, avp); 529 break; 530 531 case MATCHCLASS: 532 /* match any token in a class */ 533 class = rp[1]; 534 if (!isalpha(class)) 535 goto fail; 536 if (isupper(class)) 537 class -= 'A'; 538 else 539 class -= 'a'; 540 s = stab(ap, ST_CLASS, ST_FIND); 541 if (s == NULL || (s->s_class & (1 << class)) == 0) 542 goto fail; 543 break; 544 545 default: 546 /* must have exact match */ 547 if (!sameword(rp, ap)) 548 goto fail; 549 break; 550 } 551 552 /* successful match on this token */ 553 avp++; 554 rvp++; 555 continue; 556 557 fail: 558 /* match failed -- back up */ 559 while (--rvp >= rwr->r_lhs) 560 { 561 rp = *rvp; 562 if (*rp == MATCHANY) 563 break; 564 565 /* can't extend match: back up everything */ 566 avp--; 567 568 if (*rp == MATCHONE) 569 { 570 /* undo binding */ 571 setmatch(mlist, rp[1], (char **) NULL, (char **) NULL); 572 } 573 } 574 575 if (rvp < rwr->r_lhs) 576 { 577 /* total failure to match */ 578 break; 579 } 580 } 581 582 /* 583 ** See if we successfully matched 584 */ 585 586 if (rvp >= rwr->r_lhs && *rvp == NULL) 587 { 588 # ifdef DEBUG 589 if (Debug > 10) 590 { 591 printf("-----rule matches:\n"); 592 printav(rwr->r_rhs); 593 } 594 # endif DEBUG 595 596 /* substitute */ 597 for (rvp = rwr->r_rhs, avp = npvp; *rvp != NULL; rvp++) 598 { 599 rp = *rvp; 600 if (*rp == MATCHANY) 601 { 602 register struct match *m; 603 register char **pp; 604 extern struct match *findmatch(); 605 606 m = findmatch(mlist, rp[1]); 607 if (m != NULL) 608 { 609 pp = m->firsttok; 610 do 611 { 612 if (avp >= &npvp[MAXATOM]) 613 { 614 syserr("rewrite: expansion too long"); 615 return; 616 } 617 *avp++ = *pp; 618 } while (pp++ != m->lasttok); 619 } 620 } 621 else 622 { 623 if (avp >= &npvp[MAXATOM]) 624 { 625 syserr("rewrite: expansion too long"); 626 return; 627 } 628 *avp++ = rp; 629 } 630 } 631 *avp++ = NULL; 632 bmove((char *) npvp, (char *) pvp, (avp - npvp) * sizeof *avp); 633 # ifdef DEBUG 634 if (Debug > 3) 635 { 636 char **vp; 637 638 printf("rewritten as `"); 639 for (vp = pvp; *vp != NULL; vp++) 640 { 641 if (vp != pvp) 642 printf("_"); 643 xputs(*vp); 644 } 645 printf("'\n"); 646 } 647 # endif DEBUG 648 if (pvp[0][0] == CANONNET) 649 break; 650 } 651 else 652 { 653 # ifdef DEBUG 654 if (Debug > 10) 655 printf("----- rule fails\n"); 656 # endif DEBUG 657 rwr = rwr->r_next; 658 } 659 } 660 } 661 /* 662 ** SETMATCH -- set parameter value in match vector 663 ** 664 ** Parameters: 665 ** mlist -- list of match values. 666 ** name -- the character name of this parameter. 667 ** first -- the first location of the replacement. 668 ** last -- the last location of the replacement. 669 ** 670 ** If last == NULL, delete this entry. 671 ** If first == NULL, extend this entry (or add it if 672 ** it does not exist). 673 ** 674 ** Returns: 675 ** nothing. 676 ** 677 ** Side Effects: 678 ** munges with mlist. 679 */ 680 681 setmatch(mlist, name, first, last) 682 struct match *mlist; 683 char name; 684 char **first; 685 char **last; 686 { 687 register struct match *m; 688 struct match *nullm = NULL; 689 690 for (m = mlist; m < &mlist[MAXMATCH]; m++) 691 { 692 if (m->name == name) 693 break; 694 if (m->name == '\0') 695 nullm = m; 696 } 697 698 if (m >= &mlist[MAXMATCH]) 699 m = nullm; 700 701 if (last == NULL) 702 { 703 m->name = '\0'; 704 return; 705 } 706 707 if (m->name == '\0') 708 { 709 if (first == NULL) 710 m->firsttok = last; 711 else 712 m->firsttok = first; 713 } 714 m->name = name; 715 m->lasttok = last; 716 } 717 /* 718 ** FINDMATCH -- find match in mlist 719 ** 720 ** Parameters: 721 ** mlist -- list to search. 722 ** name -- name to find. 723 ** 724 ** Returns: 725 ** pointer to match structure. 726 ** NULL if no match. 727 ** 728 ** Side Effects: 729 ** none. 730 */ 731 732 struct match * 733 findmatch(mlist, name) 734 struct match *mlist; 735 char name; 736 { 737 register struct match *m; 738 739 for (m = mlist; m < &mlist[MAXMATCH]; m++) 740 { 741 if (m->name == name) 742 return (m); 743 } 744 745 return (NULL); 746 } 747 /* 748 ** CLRMATCH -- clear match list 749 ** 750 ** Parameters: 751 ** mlist -- list to clear. 752 ** 753 ** Returns: 754 ** none. 755 ** 756 ** Side Effects: 757 ** mlist is cleared. 758 */ 759 760 clrmatch(mlist) 761 struct match *mlist; 762 { 763 register struct match *m; 764 765 for (m = mlist; m < &mlist[MAXMATCH]; m++) 766 m->name = '\0'; 767 } 768 /* 769 ** BUILDADDR -- build address from token vector. 770 ** 771 ** Parameters: 772 ** tv -- token vector. 773 ** a -- pointer to address descriptor to fill. 774 ** If NULL, one will be allocated. 775 ** 776 ** Returns: 777 ** NULL if there was an error. 778 ** 'a' otherwise. 779 ** 780 ** Side Effects: 781 ** fills in 'a' 782 */ 783 784 ADDRESS * 785 buildaddr(tv, a) 786 register char **tv; 787 register ADDRESS *a; 788 { 789 register int i; 790 static char buf[MAXNAME]; 791 struct mailer **mp; 792 register struct mailer *m; 793 794 if (a == NULL) 795 a = (ADDRESS *) xalloc(sizeof *a); 796 a->q_flags = 0; 797 a->q_home = NULL; 798 799 /* figure out what net/mailer to use */ 800 if (**tv != CANONNET) 801 { 802 syserr("buildaddr: no net"); 803 return (NULL); 804 } 805 tv++; 806 if (strcmp(*tv, "error") == 0) 807 { 808 if (**++tv != CANONUSER) 809 syserr("buildaddr: error: no user"); 810 buf[0] = '\0'; 811 while (*++tv != NULL) 812 { 813 if (buf[0] != '\0') 814 strcat(buf, " "); 815 strcat(buf, *tv); 816 } 817 usrerr(buf); 818 return (NULL); 819 } 820 for (mp = Mailer, i = 0; (m = *mp++) != NULL; i++) 821 { 822 if (strcmp(m->m_name, *tv) == 0) 823 break; 824 } 825 if (m == NULL) 826 { 827 syserr("buildaddr: unknown net %s", *tv); 828 return (NULL); 829 } 830 a->q_mailer = i; 831 832 /* figure out what host (if any) */ 833 tv++; 834 if (!bitset(M_LOCAL, m->m_flags)) 835 { 836 if (**tv != CANONHOST) 837 { 838 syserr("buildaddr: no host"); 839 return (NULL); 840 } 841 tv++; 842 a->q_host = *tv; 843 tv++; 844 } 845 else 846 a->q_host = NULL; 847 848 /* figure out the user */ 849 if (**tv != CANONUSER) 850 { 851 syserr("buildaddr: no user"); 852 return (NULL); 853 } 854 cataddr(++tv, buf, sizeof buf); 855 a->q_user = buf; 856 857 return (a); 858 } 859 /* 860 ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 861 ** 862 ** Parameters: 863 ** pvp -- parameter vector to rebuild. 864 ** buf -- buffer to build the string into. 865 ** sz -- size of buf. 866 ** 867 ** Returns: 868 ** none. 869 ** 870 ** Side Effects: 871 ** Destroys buf. 872 */ 873 874 cataddr(pvp, buf, sz) 875 char **pvp; 876 char *buf; 877 register int sz; 878 { 879 bool oatomtok = FALSE; 880 bool natomtok = FALSE; 881 register int i; 882 register char *p; 883 884 p = buf; 885 sz--; 886 while (*pvp != NULL && (i = strlen(*pvp)) < sz) 887 { 888 natomtok = (toktype(**pvp) == ATOM); 889 if (oatomtok && natomtok) 890 *p++ = SPACESUB; 891 (void) strcpy(p, *pvp); 892 oatomtok = natomtok; 893 p += i; 894 sz -= i; 895 pvp++; 896 } 897 *p = '\0'; 898 } 899 /* 900 ** SAMEADDR -- Determine if two addresses are the same 901 ** 902 ** This is not just a straight comparison -- if the mailer doesn't 903 ** care about the host we just ignore it, etc. 904 ** 905 ** Parameters: 906 ** a, b -- pointers to the internal forms to compare. 907 ** wildflg -- if TRUE, 'a' may have no user specified, 908 ** in which case it is to match anything. 909 ** 910 ** Returns: 911 ** TRUE -- they represent the same mailbox. 912 ** FALSE -- they don't. 913 ** 914 ** Side Effects: 915 ** none. 916 */ 917 918 bool 919 sameaddr(a, b, wildflg) 920 register ADDRESS *a; 921 register ADDRESS *b; 922 bool wildflg; 923 { 924 /* if they don't have the same mailer, forget it */ 925 if (a->q_mailer != b->q_mailer) 926 return (FALSE); 927 928 /* if the user isn't the same, we can drop out */ 929 if ((!wildflg || a->q_user[0] != '\0') && strcmp(a->q_user, b->q_user) != 0) 930 return (FALSE); 931 932 /* if the mailer ignores hosts, we have succeeded! */ 933 if (bitset(M_LOCAL, Mailer[a->q_mailer]->m_flags)) 934 return (TRUE); 935 936 /* otherwise compare hosts (but be careful for NULL ptrs) */ 937 if (a->q_host == NULL || b->q_host == NULL) 938 return (FALSE); 939 if (strcmp(a->q_host, b->q_host) != 0) 940 return (FALSE); 941 942 return (TRUE); 943 } 944 /* 945 ** PRINTADDR -- print address (for debugging) 946 ** 947 ** Parameters: 948 ** a -- the address to print 949 ** follow -- follow the q_next chain. 950 ** 951 ** Returns: 952 ** none. 953 ** 954 ** Side Effects: 955 ** none. 956 */ 957 958 # ifdef DEBUG 959 960 printaddr(a, follow) 961 register ADDRESS *a; 962 bool follow; 963 { 964 while (a != NULL) 965 { 966 printf("addr@%x: ", a); 967 (void) fflush(stdout); 968 printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr, 969 a->q_mailer, Mailer[a->q_mailer]->m_name, a->q_host, a->q_user); 970 printf("\tnext=%x flags=%o, rmailer %d\n", a->q_next, 971 a->q_flags, a->q_rmailer); 972 973 if (!follow) 974 return; 975 a = a->q_next; 976 } 977 } 978 979 # endif DEBUG 980