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