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