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