1 # include "sendmail.h" 2 3 static char SccsId[] = "@(#)parseaddr.c 3.22 08/31/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 # define SPACESUB ('.'|0200) /* substitution for <lwsp> */ 43 44 ADDRESS * 45 parse(addr, a, copyf) 46 char *addr; 47 register ADDRESS *a; 48 int copyf; 49 { 50 register char **pvp; 51 register struct mailer *m; 52 extern char **prescan(); 53 extern ADDRESS *buildaddr(); 54 55 /* 56 ** Initialize and prescan address. 57 */ 58 59 To = addr; 60 # ifdef DEBUG 61 if (Debug) 62 printf("\n--parse(%s)\n", addr); 63 # endif DEBUG 64 65 pvp = prescan(addr, '\0'); 66 if (pvp == NULL) 67 return (NULL); 68 69 /* 70 ** Apply rewriting rules. 71 */ 72 73 rewrite(pvp, 0); 74 75 /* 76 ** See if we resolved to a real mailer. 77 */ 78 79 if (pvp[0][0] != CANONNET) 80 { 81 setstat(EX_USAGE); 82 usrerr("cannot resolve name"); 83 return (NULL); 84 } 85 86 /* 87 ** Build canonical address from pvp. 88 */ 89 90 a = buildaddr(pvp, a); 91 if (a == NULL) 92 return (NULL); 93 m = Mailer[a->q_mailer]; 94 95 /* 96 ** Make local copies of the host & user and then 97 ** transport them out. 98 */ 99 100 if (copyf > 0) 101 a->q_paddr = newstr(addr); 102 else 103 a->q_paddr = addr; 104 105 if (copyf >= 0) 106 { 107 if (a->q_host != NULL) 108 a->q_host = newstr(a->q_host); 109 else 110 a->q_host = ""; 111 if (a->q_user != a->q_paddr) 112 a->q_user = newstr(a->q_user); 113 } 114 115 /* 116 ** Do UPPER->lower case mapping unless inhibited. 117 */ 118 119 if (!bitset(M_HST_UPPER, m->m_flags)) 120 makelower(a->q_host); 121 if (!bitset(M_USR_UPPER, m->m_flags)) 122 makelower(a->q_user); 123 124 /* 125 ** Compute return value. 126 */ 127 128 # ifdef DEBUG 129 if (Debug) 130 printf("parse(\"%s\"): host \"%s\" user \"%s\" mailer %d\n", 131 addr, a->q_host, a->q_user, a->q_mailer); 132 # endif DEBUG 133 134 return (a); 135 } 136 /* 137 ** PRESCAN -- Prescan name and make it canonical 138 ** 139 ** Scans a name and turns it into canonical form. This involves 140 ** deleting blanks, comments (in parentheses), and turning the 141 ** word "at" into an at-sign ("@"). The name is copied as this 142 ** is done; it is legal to copy a name onto itself, since this 143 ** process can only make things smaller. 144 ** 145 ** This routine knows about quoted strings and angle brackets. 146 ** 147 ** There are certain subtleties to this routine. The one that 148 ** comes to mind now is that backslashes on the ends of names 149 ** are silently stripped off; this is intentional. The problem 150 ** is that some versions of sndmsg (like at LBL) set the kill 151 ** character to something other than @ when reading addresses; 152 ** so people type "csvax.eric\@berkeley" -- which screws up the 153 ** berknet mailer. 154 ** 155 ** Parameters: 156 ** addr -- the name to chomp. 157 ** delim -- the delimiter for the address, normally 158 ** '\0' or ','; \0 is accepted in any case. 159 ** are moving in place; set buflim to high core. 160 ** 161 ** Returns: 162 ** A pointer to a vector of tokens. 163 ** NULL on error. 164 ** 165 ** Side Effects: 166 ** none. 167 */ 168 169 # define OPER 1 170 # define ATOM 2 171 # define EOTOK 3 172 # define QSTRING 4 173 # define SPACE 5 174 # define DOLLAR 6 175 # define GETONE 7 176 177 char ** 178 prescan(addr, delim) 179 char *addr; 180 char delim; 181 { 182 register char *p; 183 static char buf[MAXNAME+MAXATOM]; 184 static char *av[MAXATOM+1]; 185 char **avp; 186 bool space; 187 bool bslashmode; 188 int cmntcnt; 189 int brccnt; 190 register char c; 191 char *tok; 192 register char *q; 193 register int state; 194 int nstate; 195 extern char lower(); 196 197 space = FALSE; 198 q = buf; 199 bslashmode = FALSE; 200 cmntcnt = brccnt = 0; 201 avp = av; 202 state = OPER; 203 for (p = addr; *p != '\0' && *p != delim; ) 204 { 205 /* read a token */ 206 tok = q; 207 while ((c = *p++) != '\0' && c != delim) 208 { 209 /* chew up special characters */ 210 c &= ~0200; 211 *q = '\0'; 212 if (bslashmode) 213 { 214 c |= 0200; 215 bslashmode = FALSE; 216 } 217 else if (c == '\\') 218 { 219 bslashmode = TRUE; 220 continue; 221 } 222 else if (c == '"') 223 { 224 if (state == QSTRING) 225 state = OPER; 226 else 227 state = QSTRING; 228 break; 229 } 230 231 nstate = toktype(c); 232 switch (state) 233 { 234 case QSTRING: /* in quoted string */ 235 break; 236 237 case ATOM: /* regular atom */ 238 /* state = nstate; */ 239 if (nstate != ATOM) 240 { 241 state = EOTOK; 242 p--; 243 } 244 break; 245 246 case GETONE: /* grab one character */ 247 state = OPER; 248 break; 249 250 case EOTOK: /* after atom or q-string */ 251 state = nstate; 252 if (state == SPACE) 253 continue; 254 break; 255 256 case SPACE: /* linear white space */ 257 state = nstate; 258 space = TRUE; 259 break; 260 261 case OPER: /* operator */ 262 if (nstate == SPACE) 263 continue; 264 state = nstate; 265 break; 266 267 case DOLLAR: /* $- etc. */ 268 state = OPER; 269 switch (c) 270 { 271 case '$': /* literal $ */ 272 break; 273 274 case '+': /* match anything */ 275 c = MATCHANY; 276 state = GETONE; 277 break; 278 279 case '-': /* match one token */ 280 c = MATCHONE; 281 state = GETONE; 282 break; 283 284 case '=': /* match one token of class */ 285 c = MATCHCLASS; 286 state = GETONE; 287 break; 288 289 case '#': /* canonical net name */ 290 c = CANONNET; 291 break; 292 293 case '@': /* canonical host name */ 294 c = CANONHOST; 295 break; 296 297 case ':': /* canonical user name */ 298 c = CANONUSER; 299 break; 300 301 default: 302 c = '$'; 303 state = OPER; 304 p--; 305 break; 306 } 307 break; 308 309 default: 310 syserr("prescan: unknown state %d", state); 311 } 312 313 if (state == EOTOK || state == SPACE) 314 break; 315 if (c == '$' && delim == '\t') 316 { 317 state = DOLLAR; 318 continue; 319 } 320 321 /* squirrel it away */ 322 if (q >= &buf[sizeof buf - 5]) 323 { 324 usrerr("Address too long"); 325 return (NULL); 326 } 327 *q++ = c; 328 329 /* decide whether this represents end of token */ 330 if (state == OPER) 331 break; 332 } 333 if (c == '\0' || c == delim) 334 p--; 335 336 /* new token */ 337 if (tok == q) 338 continue; 339 *q++ = '\0'; 340 341 c = tok[0]; 342 if (c == '(') 343 { 344 cmntcnt++; 345 continue; 346 } 347 else if (c == ')') 348 { 349 if (cmntcnt <= 0) 350 { 351 usrerr("Unbalanced ')'"); 352 return (NULL); 353 } 354 else 355 { 356 cmntcnt--; 357 continue; 358 } 359 } 360 else if (cmntcnt > 0) 361 continue; 362 363 *avp++ = tok; 364 365 /* we prefer <> specs */ 366 if (c == '<') 367 { 368 if (brccnt < 0) 369 { 370 usrerr("multiple < spec"); 371 return (NULL); 372 } 373 brccnt++; 374 space = FALSE; 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 *avp++ = *pp; 613 } while (pp++ != m->lasttok); 614 } 615 } 616 else 617 *avp++ = rp; 618 } 619 *avp++ = NULL; 620 bmove((char *) npvp, (char *) pvp, (avp - npvp) * sizeof *avp); 621 # ifdef DEBUG 622 if (Debug > 3) 623 { 624 char **vp; 625 626 printf("rewritten as `"); 627 for (vp = pvp; *vp != NULL; vp++) 628 { 629 if (vp != pvp) 630 printf("_"); 631 xputs(*vp); 632 } 633 printf("'\n"); 634 } 635 # endif DEBUG 636 if (pvp[0][0] == CANONNET) 637 break; 638 } 639 else 640 { 641 # ifdef DEBUG 642 if (Debug > 10) 643 printf("----- rule fails\n"); 644 # endif DEBUG 645 rwr = rwr->r_next; 646 } 647 } 648 } 649 /* 650 ** SETMATCH -- set parameter value in match vector 651 ** 652 ** Parameters: 653 ** mlist -- list of match values. 654 ** name -- the character name of this parameter. 655 ** first -- the first location of the replacement. 656 ** last -- the last location of the replacement. 657 ** 658 ** If last == NULL, delete this entry. 659 ** If first == NULL, extend this entry (or add it if 660 ** it does not exist). 661 ** 662 ** Returns: 663 ** nothing. 664 ** 665 ** Side Effects: 666 ** munges with mlist. 667 */ 668 669 setmatch(mlist, name, first, last) 670 struct match *mlist; 671 char name; 672 char **first; 673 char **last; 674 { 675 register struct match *m; 676 struct match *nullm = NULL; 677 678 for (m = mlist; m < &mlist[MAXMATCH]; m++) 679 { 680 if (m->name == name) 681 break; 682 if (m->name == '\0') 683 nullm = m; 684 } 685 686 if (m >= &mlist[MAXMATCH]) 687 m = nullm; 688 689 if (last == NULL) 690 { 691 m->name = '\0'; 692 return; 693 } 694 695 if (m->name == '\0') 696 { 697 if (first == NULL) 698 m->firsttok = last; 699 else 700 m->firsttok = first; 701 } 702 m->name = name; 703 m->lasttok = last; 704 } 705 /* 706 ** FINDMATCH -- find match in mlist 707 ** 708 ** Parameters: 709 ** mlist -- list to search. 710 ** name -- name to find. 711 ** 712 ** Returns: 713 ** pointer to match structure. 714 ** NULL if no match. 715 ** 716 ** Side Effects: 717 ** none. 718 */ 719 720 struct match * 721 findmatch(mlist, name) 722 struct match *mlist; 723 char name; 724 { 725 register struct match *m; 726 727 for (m = mlist; m < &mlist[MAXMATCH]; m++) 728 { 729 if (m->name == name) 730 return (m); 731 } 732 733 return (NULL); 734 } 735 /* 736 ** CLRMATCH -- clear match list 737 ** 738 ** Parameters: 739 ** mlist -- list to clear. 740 ** 741 ** Returns: 742 ** none. 743 ** 744 ** Side Effects: 745 ** mlist is cleared. 746 */ 747 748 clrmatch(mlist) 749 struct match *mlist; 750 { 751 register struct match *m; 752 753 for (m = mlist; m < &mlist[MAXMATCH]; m++) 754 m->name = '\0'; 755 } 756 /* 757 ** BUILDADDR -- build address from token vector. 758 ** 759 ** Parameters: 760 ** tv -- token vector. 761 ** a -- pointer to address descriptor to fill. 762 ** If NULL, one will be allocated. 763 ** 764 ** Returns: 765 ** NULL if there was an error. 766 ** 'a' otherwise. 767 ** 768 ** Side Effects: 769 ** fills in 'a' 770 */ 771 772 ADDRESS * 773 buildaddr(tv, a) 774 register char **tv; 775 register ADDRESS *a; 776 { 777 register int i; 778 static char buf[MAXNAME]; 779 struct mailer **mp; 780 register struct mailer *m; 781 782 if (a == NULL) 783 a = (ADDRESS *) xalloc(sizeof *a); 784 a->q_flags = 0; 785 a->q_home = NULL; 786 787 /* figure out what net/mailer to use */ 788 if (**tv != CANONNET) 789 { 790 syserr("buildaddr: no net"); 791 return (NULL); 792 } 793 tv++; 794 if (strcmp(*tv, "error") == 0) 795 { 796 if (**++tv != CANONUSER) 797 syserr("buildaddr: error: no user"); 798 buf[0] = '\0'; 799 while (*++tv != NULL) 800 { 801 if (buf[0] != '\0') 802 strcat(buf, " "); 803 strcat(buf, *tv); 804 } 805 usrerr(buf); 806 return (NULL); 807 } 808 for (mp = Mailer, i = 0; (m = *mp++) != NULL; i++) 809 { 810 if (strcmp(m->m_name, *tv) == 0) 811 break; 812 } 813 if (m == NULL) 814 { 815 syserr("buildaddr: unknown net %s", *tv); 816 return (NULL); 817 } 818 a->q_mailer = i; 819 820 /* figure out what host (if any) */ 821 tv++; 822 if (!bitset(M_LOCAL, m->m_flags)) 823 { 824 if (**tv != CANONHOST) 825 { 826 syserr("buildaddr: no host"); 827 return (NULL); 828 } 829 tv++; 830 a->q_host = *tv; 831 tv++; 832 } 833 else 834 a->q_host = NULL; 835 836 /* figure out the user */ 837 if (**tv != CANONUSER) 838 { 839 syserr("buildaddr: no user"); 840 return (NULL); 841 } 842 cataddr(++tv, buf, sizeof buf); 843 a->q_user = buf; 844 845 return (a); 846 } 847 /* 848 ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 849 ** 850 ** Parameters: 851 ** pvp -- parameter vector to rebuild. 852 ** buf -- buffer to build the string into. 853 ** sz -- size of buf. 854 ** 855 ** Returns: 856 ** none. 857 ** 858 ** Side Effects: 859 ** Destroys buf. 860 */ 861 862 cataddr(pvp, buf, sz) 863 char **pvp; 864 char *buf; 865 register int sz; 866 { 867 bool oatomtok = FALSE; 868 bool natomtok = FALSE; 869 register int i; 870 register char *p; 871 872 p = buf; 873 sz--; 874 while (*pvp != NULL && (i = strlen(*pvp)) < sz) 875 { 876 natomtok = (toktype(**pvp) == ATOM); 877 if (oatomtok && natomtok) 878 *p++ = SPACESUB; 879 (void) strcpy(p, *pvp); 880 oatomtok = natomtok; 881 p += i; 882 sz -= i; 883 pvp++; 884 } 885 *p = '\0'; 886 } 887 /* 888 ** SAMEADDR -- Determine if two addresses are the same 889 ** 890 ** This is not just a straight comparison -- if the mailer doesn't 891 ** care about the host we just ignore it, etc. 892 ** 893 ** Parameters: 894 ** a, b -- pointers to the internal forms to compare. 895 ** wildflg -- if TRUE, 'a' may have no user specified, 896 ** in which case it is to match anything. 897 ** 898 ** Returns: 899 ** TRUE -- they represent the same mailbox. 900 ** FALSE -- they don't. 901 ** 902 ** Side Effects: 903 ** none. 904 */ 905 906 bool 907 sameaddr(a, b, wildflg) 908 register ADDRESS *a; 909 register ADDRESS *b; 910 bool wildflg; 911 { 912 /* if they don't have the same mailer, forget it */ 913 if (a->q_mailer != b->q_mailer) 914 return (FALSE); 915 916 /* if the user isn't the same, we can drop out */ 917 if ((!wildflg || a->q_user[0] != '\0') && strcmp(a->q_user, b->q_user) != 0) 918 return (FALSE); 919 920 /* if the mailer ignores hosts, we have succeeded! */ 921 if (bitset(M_LOCAL, Mailer[a->q_mailer]->m_flags)) 922 return (TRUE); 923 924 /* otherwise compare hosts (but be careful for NULL ptrs) */ 925 if (a->q_host == NULL || b->q_host == NULL) 926 return (FALSE); 927 if (strcmp(a->q_host, b->q_host) != 0) 928 return (FALSE); 929 930 return (TRUE); 931 } 932 /* 933 ** PRINTADDR -- print address (for debugging) 934 ** 935 ** Parameters: 936 ** a -- the address to print 937 ** follow -- follow the q_next chain. 938 ** 939 ** Returns: 940 ** none. 941 ** 942 ** Side Effects: 943 ** none. 944 */ 945 946 printaddr(a, follow) 947 register ADDRESS *a; 948 bool follow; 949 { 950 while (a != NULL) 951 { 952 printf("addr@%x: ", a); 953 (void) fflush(stdout); 954 printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr, 955 a->q_mailer, Mailer[a->q_mailer]->m_name, a->q_host, a->q_user); 956 printf("\tnext=%x flags=%o, rmailer %d\n", a->q_next, 957 a->q_flags, a->q_rmailer); 958 959 if (!follow) 960 return; 961 a = a->q_next; 962 } 963 } 964