1 # include "sendmail.h" 2 3 static char SccsId[] = "@(#)parseaddr.c 3.24 09/12/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 *avp++ = tok; 360 361 /* we prefer <> specs */ 362 if (c == '<') 363 { 364 if (brccnt < 0) 365 { 366 usrerr("multiple < spec"); 367 return (NULL); 368 } 369 brccnt++; 370 if (brccnt == 1) 371 { 372 /* we prefer using machine readable name */ 373 q = buf; 374 *q = '\0'; 375 avp = av; 376 continue; 377 } 378 } 379 else if (c == '>') 380 { 381 if (brccnt <= 0) 382 { 383 usrerr("Unbalanced `>'"); 384 return (NULL); 385 } 386 else 387 brccnt--; 388 if (brccnt <= 0) 389 { 390 brccnt = -1; 391 continue; 392 } 393 } 394 } 395 *avp = NULL; 396 if (cmntcnt > 0) 397 usrerr("Unbalanced '('"); 398 else if (brccnt > 0) 399 usrerr("Unbalanced '<'"); 400 else if (state == QSTRING) 401 usrerr("Unbalanced '\"'"); 402 else if (av[0] != NULL) 403 return (av); 404 return (NULL); 405 } 406 /* 407 ** TOKTYPE -- return token type 408 ** 409 ** Parameters: 410 ** c -- the character in question. 411 ** 412 ** Returns: 413 ** Its type. 414 ** 415 ** Side Effects: 416 ** none. 417 */ 418 419 toktype(c) 420 register char c; 421 { 422 static char buf[50]; 423 static bool firstime = TRUE; 424 425 if (firstime) 426 { 427 firstime = FALSE; 428 (void) expand("$o", buf, &buf[sizeof buf - 1]); 429 strcat(buf, DELIMCHARS); 430 } 431 if (!isascii(c)) 432 return (ATOM); 433 if (isspace(c)) 434 return (SPACE); 435 if (iscntrl(c) || index(buf, c) != NULL) 436 return (OPER); 437 return (ATOM); 438 } 439 /* 440 ** REWRITE -- apply rewrite rules to token vector. 441 ** 442 ** Parameters: 443 ** pvp -- pointer to token vector. 444 ** 445 ** Returns: 446 ** none. 447 ** 448 ** Side Effects: 449 ** pvp is modified. 450 */ 451 452 struct match 453 { 454 char **firsttok; /* first token matched */ 455 char **lasttok; /* last token matched */ 456 char name; /* name of parameter */ 457 }; 458 459 # define MAXMATCH 8 /* max params per rewrite */ 460 461 462 rewrite(pvp, ruleset) 463 char **pvp; 464 int ruleset; 465 { 466 register char *ap; /* address pointer */ 467 register char *rp; /* rewrite pointer */ 468 register char **avp; /* address vector pointer */ 469 register char **rvp; /* rewrite vector pointer */ 470 struct rewrite *rwr; 471 struct match mlist[MAXMATCH]; 472 char *npvp[MAXATOM+1]; /* temporary space for rebuild */ 473 extern bool sameword(); 474 475 # ifdef DEBUG 476 if (Debug > 9) 477 { 478 printf("rewrite: original pvp:\n"); 479 printav(pvp); 480 } 481 # endif DEBUG 482 483 /* 484 ** Run through the list of rewrite rules, applying 485 ** any that match. 486 */ 487 488 for (rwr = RewriteRules[ruleset]; rwr != NULL; ) 489 { 490 # ifdef DEBUG 491 if (Debug > 10) 492 { 493 printf("-----trying rule:\n"); 494 printav(rwr->r_lhs); 495 } 496 # endif DEBUG 497 498 /* try to match on this rule */ 499 clrmatch(mlist); 500 for (rvp = rwr->r_lhs, avp = pvp; *avp != NULL; ) 501 { 502 ap = *avp; 503 rp = *rvp; 504 505 if (rp == NULL) 506 { 507 /* end-of-pattern before end-of-address */ 508 goto fail; 509 } 510 511 switch (*rp) 512 { 513 register STAB *s; 514 register int class; 515 516 case MATCHONE: 517 /* match exactly one token */ 518 setmatch(mlist, rp[1], avp, avp); 519 break; 520 521 case MATCHANY: 522 /* match any number of tokens */ 523 setmatch(mlist, rp[1], (char **) NULL, avp); 524 break; 525 526 case MATCHCLASS: 527 /* match any token in a class */ 528 class = rp[1]; 529 if (!isalpha(class)) 530 goto fail; 531 if (isupper(class)) 532 class -= 'A'; 533 else 534 class -= 'a'; 535 s = stab(ap, ST_CLASS, ST_FIND); 536 if (s == NULL || (s->s_class & (1 << class)) == 0) 537 goto fail; 538 break; 539 540 default: 541 /* must have exact match */ 542 if (!sameword(rp, ap)) 543 goto fail; 544 break; 545 } 546 547 /* successful match on this token */ 548 avp++; 549 rvp++; 550 continue; 551 552 fail: 553 /* match failed -- back up */ 554 while (--rvp >= rwr->r_lhs) 555 { 556 rp = *rvp; 557 if (*rp == MATCHANY) 558 break; 559 560 /* can't extend match: back up everything */ 561 avp--; 562 563 if (*rp == MATCHONE) 564 { 565 /* undo binding */ 566 setmatch(mlist, rp[1], (char **) NULL, (char **) NULL); 567 } 568 } 569 570 if (rvp < rwr->r_lhs) 571 { 572 /* total failure to match */ 573 break; 574 } 575 } 576 577 /* 578 ** See if we successfully matched 579 */ 580 581 if (rvp >= rwr->r_lhs && *rvp == NULL) 582 { 583 # ifdef DEBUG 584 if (Debug > 10) 585 { 586 printf("-----rule matches:\n"); 587 printav(rwr->r_rhs); 588 } 589 # endif DEBUG 590 591 /* substitute */ 592 for (rvp = rwr->r_rhs, avp = npvp; *rvp != NULL; rvp++) 593 { 594 rp = *rvp; 595 if (*rp == MATCHANY) 596 { 597 register struct match *m; 598 register char **pp; 599 extern struct match *findmatch(); 600 601 m = findmatch(mlist, rp[1]); 602 if (m != NULL) 603 { 604 pp = m->firsttok; 605 do 606 { 607 *avp++ = *pp; 608 } while (pp++ != m->lasttok); 609 } 610 } 611 else 612 *avp++ = rp; 613 } 614 *avp++ = NULL; 615 bmove((char *) npvp, (char *) pvp, (avp - npvp) * sizeof *avp); 616 # ifdef DEBUG 617 if (Debug > 3) 618 { 619 char **vp; 620 621 printf("rewritten as `"); 622 for (vp = pvp; *vp != NULL; vp++) 623 { 624 if (vp != pvp) 625 printf("_"); 626 xputs(*vp); 627 } 628 printf("'\n"); 629 } 630 # endif DEBUG 631 if (pvp[0][0] == CANONNET) 632 break; 633 } 634 else 635 { 636 # ifdef DEBUG 637 if (Debug > 10) 638 printf("----- rule fails\n"); 639 # endif DEBUG 640 rwr = rwr->r_next; 641 } 642 } 643 } 644 /* 645 ** SETMATCH -- set parameter value in match vector 646 ** 647 ** Parameters: 648 ** mlist -- list of match values. 649 ** name -- the character name of this parameter. 650 ** first -- the first location of the replacement. 651 ** last -- the last location of the replacement. 652 ** 653 ** If last == NULL, delete this entry. 654 ** If first == NULL, extend this entry (or add it if 655 ** it does not exist). 656 ** 657 ** Returns: 658 ** nothing. 659 ** 660 ** Side Effects: 661 ** munges with mlist. 662 */ 663 664 setmatch(mlist, name, first, last) 665 struct match *mlist; 666 char name; 667 char **first; 668 char **last; 669 { 670 register struct match *m; 671 struct match *nullm = NULL; 672 673 for (m = mlist; m < &mlist[MAXMATCH]; m++) 674 { 675 if (m->name == name) 676 break; 677 if (m->name == '\0') 678 nullm = m; 679 } 680 681 if (m >= &mlist[MAXMATCH]) 682 m = nullm; 683 684 if (last == NULL) 685 { 686 m->name = '\0'; 687 return; 688 } 689 690 if (m->name == '\0') 691 { 692 if (first == NULL) 693 m->firsttok = last; 694 else 695 m->firsttok = first; 696 } 697 m->name = name; 698 m->lasttok = last; 699 } 700 /* 701 ** FINDMATCH -- find match in mlist 702 ** 703 ** Parameters: 704 ** mlist -- list to search. 705 ** name -- name to find. 706 ** 707 ** Returns: 708 ** pointer to match structure. 709 ** NULL if no match. 710 ** 711 ** Side Effects: 712 ** none. 713 */ 714 715 struct match * 716 findmatch(mlist, name) 717 struct match *mlist; 718 char name; 719 { 720 register struct match *m; 721 722 for (m = mlist; m < &mlist[MAXMATCH]; m++) 723 { 724 if (m->name == name) 725 return (m); 726 } 727 728 return (NULL); 729 } 730 /* 731 ** CLRMATCH -- clear match list 732 ** 733 ** Parameters: 734 ** mlist -- list to clear. 735 ** 736 ** Returns: 737 ** none. 738 ** 739 ** Side Effects: 740 ** mlist is cleared. 741 */ 742 743 clrmatch(mlist) 744 struct match *mlist; 745 { 746 register struct match *m; 747 748 for (m = mlist; m < &mlist[MAXMATCH]; m++) 749 m->name = '\0'; 750 } 751 /* 752 ** BUILDADDR -- build address from token vector. 753 ** 754 ** Parameters: 755 ** tv -- token vector. 756 ** a -- pointer to address descriptor to fill. 757 ** If NULL, one will be allocated. 758 ** 759 ** Returns: 760 ** NULL if there was an error. 761 ** 'a' otherwise. 762 ** 763 ** Side Effects: 764 ** fills in 'a' 765 */ 766 767 ADDRESS * 768 buildaddr(tv, a) 769 register char **tv; 770 register ADDRESS *a; 771 { 772 register int i; 773 static char buf[MAXNAME]; 774 struct mailer **mp; 775 register struct mailer *m; 776 777 if (a == NULL) 778 a = (ADDRESS *) xalloc(sizeof *a); 779 a->q_flags = 0; 780 a->q_home = NULL; 781 782 /* figure out what net/mailer to use */ 783 if (**tv != CANONNET) 784 { 785 syserr("buildaddr: no net"); 786 return (NULL); 787 } 788 tv++; 789 if (strcmp(*tv, "error") == 0) 790 { 791 if (**++tv != CANONUSER) 792 syserr("buildaddr: error: no user"); 793 buf[0] = '\0'; 794 while (*++tv != NULL) 795 { 796 if (buf[0] != '\0') 797 strcat(buf, " "); 798 strcat(buf, *tv); 799 } 800 usrerr(buf); 801 return (NULL); 802 } 803 for (mp = Mailer, i = 0; (m = *mp++) != NULL; i++) 804 { 805 if (strcmp(m->m_name, *tv) == 0) 806 break; 807 } 808 if (m == NULL) 809 { 810 syserr("buildaddr: unknown net %s", *tv); 811 return (NULL); 812 } 813 a->q_mailer = i; 814 815 /* figure out what host (if any) */ 816 tv++; 817 if (!bitset(M_LOCAL, m->m_flags)) 818 { 819 if (**tv != CANONHOST) 820 { 821 syserr("buildaddr: no host"); 822 return (NULL); 823 } 824 tv++; 825 a->q_host = *tv; 826 tv++; 827 } 828 else 829 a->q_host = NULL; 830 831 /* figure out the user */ 832 if (**tv != CANONUSER) 833 { 834 syserr("buildaddr: no user"); 835 return (NULL); 836 } 837 cataddr(++tv, buf, sizeof buf); 838 a->q_user = buf; 839 840 return (a); 841 } 842 /* 843 ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 844 ** 845 ** Parameters: 846 ** pvp -- parameter vector to rebuild. 847 ** buf -- buffer to build the string into. 848 ** sz -- size of buf. 849 ** 850 ** Returns: 851 ** none. 852 ** 853 ** Side Effects: 854 ** Destroys buf. 855 */ 856 857 cataddr(pvp, buf, sz) 858 char **pvp; 859 char *buf; 860 register int sz; 861 { 862 bool oatomtok = FALSE; 863 bool natomtok = FALSE; 864 register int i; 865 register char *p; 866 867 p = buf; 868 sz--; 869 while (*pvp != NULL && (i = strlen(*pvp)) < sz) 870 { 871 natomtok = (toktype(**pvp) == ATOM); 872 if (oatomtok && natomtok) 873 *p++ = SPACESUB; 874 (void) strcpy(p, *pvp); 875 oatomtok = natomtok; 876 p += i; 877 sz -= i; 878 pvp++; 879 } 880 *p = '\0'; 881 } 882 /* 883 ** SAMEADDR -- Determine if two addresses are the same 884 ** 885 ** This is not just a straight comparison -- if the mailer doesn't 886 ** care about the host we just ignore it, etc. 887 ** 888 ** Parameters: 889 ** a, b -- pointers to the internal forms to compare. 890 ** wildflg -- if TRUE, 'a' may have no user specified, 891 ** in which case it is to match anything. 892 ** 893 ** Returns: 894 ** TRUE -- they represent the same mailbox. 895 ** FALSE -- they don't. 896 ** 897 ** Side Effects: 898 ** none. 899 */ 900 901 bool 902 sameaddr(a, b, wildflg) 903 register ADDRESS *a; 904 register ADDRESS *b; 905 bool wildflg; 906 { 907 /* if they don't have the same mailer, forget it */ 908 if (a->q_mailer != b->q_mailer) 909 return (FALSE); 910 911 /* if the user isn't the same, we can drop out */ 912 if ((!wildflg || a->q_user[0] != '\0') && strcmp(a->q_user, b->q_user) != 0) 913 return (FALSE); 914 915 /* if the mailer ignores hosts, we have succeeded! */ 916 if (bitset(M_LOCAL, Mailer[a->q_mailer]->m_flags)) 917 return (TRUE); 918 919 /* otherwise compare hosts (but be careful for NULL ptrs) */ 920 if (a->q_host == NULL || b->q_host == NULL) 921 return (FALSE); 922 if (strcmp(a->q_host, b->q_host) != 0) 923 return (FALSE); 924 925 return (TRUE); 926 } 927 /* 928 ** PRINTADDR -- print address (for debugging) 929 ** 930 ** Parameters: 931 ** a -- the address to print 932 ** follow -- follow the q_next chain. 933 ** 934 ** Returns: 935 ** none. 936 ** 937 ** Side Effects: 938 ** none. 939 */ 940 941 # ifdef DEBUG 942 943 printaddr(a, follow) 944 register ADDRESS *a; 945 bool follow; 946 { 947 while (a != NULL) 948 { 949 printf("addr@%x: ", a); 950 (void) fflush(stdout); 951 printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr, 952 a->q_mailer, Mailer[a->q_mailer]->m_name, a->q_host, a->q_user); 953 printf("\tnext=%x flags=%o, rmailer %d\n", a->q_next, 954 a->q_flags, a->q_rmailer); 955 956 if (!follow) 957 return; 958 a = a->q_next; 959 } 960 } 961 962 # endif DEBUG 963