1 # include <stdio.h> 2 # include <ctype.h> 3 # include "sendmail.h" 4 5 static char SccsId[] = "@(#)parseaddr.c 3.13 08/08/81"; 6 7 /* 8 ** PARSE -- Parse an address 9 ** 10 ** Parses an address and breaks it up into three parts: a 11 ** net to transmit the message on, the host to transmit it 12 ** to, and a user on that host. These are loaded into an 13 ** ADDRESS header with the values squirreled away if necessary. 14 ** The "user" part may not be a real user; the process may 15 ** just reoccur on that machine. For example, on a machine 16 ** with an arpanet connection, the address 17 ** csvax.bill@berkeley 18 ** will break up to a "user" of 'csvax.bill' and a host 19 ** of 'berkeley' -- to be transmitted over the arpanet. 20 ** 21 ** Parameters: 22 ** addr -- the address to parse. 23 ** a -- a pointer to the address descriptor buffer. 24 ** If NULL, a header will be created. 25 ** copyf -- determines what shall be copied: 26 ** -1 -- don't copy anything. The printname 27 ** (q_paddr) is just addr, and the 28 ** user & host are allocated internally 29 ** to parse. 30 ** 0 -- copy out the parsed user & host, but 31 ** don't copy the printname. 32 ** +1 -- copy everything. 33 ** 34 ** Returns: 35 ** A pointer to the address descriptor header (`a' if 36 ** `a' is non-NULL). 37 ** NULL on error. 38 ** 39 ** Side Effects: 40 ** none 41 ** 42 ** Called By: 43 ** main 44 ** sendto 45 ** alias 46 ** savemail 47 */ 48 49 # define DELIMCHARS "$()<>,;\\\"\r\n" /* word delimiters */ 50 # define SPACESUB ('.'|0200) /* substitution for <lwsp> */ 51 52 ADDRESS * 53 parse(addr, a, copyf) 54 char *addr; 55 register ADDRESS *a; 56 int copyf; 57 { 58 register char **pvp; 59 register struct mailer *m; 60 extern char **prescan(); 61 extern char *newstr(); 62 extern char *strcpy(); 63 extern ADDRESS *buildaddr(); 64 65 /* 66 ** Initialize and prescan address. 67 */ 68 69 To = addr; 70 # ifdef DEBUG 71 if (Debug) 72 printf("\n--parse(%s)\n", addr); 73 # endif DEBUG 74 75 pvp = prescan(addr, '\0'); 76 if (pvp == NULL) 77 return (NULL); 78 79 /* 80 ** Apply rewriting rules. 81 */ 82 83 rewrite(pvp); 84 85 /* 86 ** See if we resolved to a real mailer. 87 */ 88 89 if (pvp[0][0] != CANONNET) 90 { 91 setstat(EX_USAGE); 92 usrerr("cannot resolve name"); 93 return (NULL); 94 } 95 96 /* 97 ** Build canonical address from pvp. 98 */ 99 100 a = buildaddr(pvp, a); 101 m = Mailer[a->q_mailer]; 102 103 /* 104 ** Make local copies of the host & user and then 105 ** transport them out. 106 */ 107 108 if (copyf > 0) 109 a->q_paddr = newstr(addr); 110 else 111 a->q_paddr = addr; 112 113 if (copyf >= 0) 114 { 115 if (a->q_host != NULL) 116 a->q_host = newstr(a->q_host); 117 else 118 a->q_host = ""; 119 if (a->q_user != a->q_paddr) 120 a->q_user = newstr(a->q_user); 121 } 122 123 /* 124 ** Do UPPER->lower case mapping unless inhibited. 125 */ 126 127 if (!bitset(M_HST_UPPER, m->m_flags)) 128 makelower(a->q_host); 129 if (!bitset(M_USR_UPPER, m->m_flags)) 130 makelower(a->q_user); 131 132 /* 133 ** Compute return value. 134 */ 135 136 # ifdef DEBUG 137 if (Debug) 138 printf("parse(\"%s\"): host \"%s\" user \"%s\" mailer %d\n", 139 addr, a->q_host, a->q_user, a->q_mailer); 140 # endif DEBUG 141 142 return (a); 143 } 144 /* 145 ** PRESCAN -- Prescan name and make it canonical 146 ** 147 ** Scans a name and turns it into canonical form. This involves 148 ** deleting blanks, comments (in parentheses), and turning the 149 ** word "at" into an at-sign ("@"). The name is copied as this 150 ** is done; it is legal to copy a name onto itself, since this 151 ** process can only make things smaller. 152 ** 153 ** This routine knows about quoted strings and angle brackets. 154 ** 155 ** There are certain subtleties to this routine. The one that 156 ** comes to mind now is that backslashes on the ends of names 157 ** are silently stripped off; this is intentional. The problem 158 ** is that some versions of sndmsg (like at LBL) set the kill 159 ** character to something other than @ when reading addresses; 160 ** so people type "csvax.eric\@berkeley" -- which screws up the 161 ** berknet mailer. 162 ** 163 ** Parameters: 164 ** addr -- the name to chomp. 165 ** delim -- the delimiter for the address, normally 166 ** '\0' or ','; \0 is accepted in any case. 167 ** are moving in place; set buflim to high core. 168 ** 169 ** Returns: 170 ** A pointer to a vector of tokens. 171 ** NULL on error. 172 ** 173 ** Side Effects: 174 ** none. 175 */ 176 177 # define OPER 1 178 # define ATOM 2 179 # define EOTOK 3 180 # define QSTRING 4 181 # define SPACE 5 182 # define DOLLAR 6 183 # define GETONE 7 184 185 char ** 186 prescan(addr, delim) 187 char *addr; 188 char delim; 189 { 190 register char *p; 191 static char buf[MAXNAME+MAXATOM]; 192 static char *av[MAXATOM+1]; 193 char **avp; 194 bool space; 195 bool bslashmode; 196 int cmntcnt; 197 int brccnt; 198 register char c; 199 char *tok; 200 register char *q; 201 extern char *index(); 202 register int state; 203 int nstate; 204 205 space = FALSE; 206 q = buf; 207 bslashmode = FALSE; 208 cmntcnt = brccnt = 0; 209 avp = av; 210 state = OPER; 211 for (p = addr; *p != '\0' && *p != delim; ) 212 { 213 /* read a token */ 214 tok = q; 215 while ((c = *p++) != '\0' && c != delim) 216 { 217 /* chew up special characters */ 218 *q = '\0'; 219 if (bslashmode) 220 { 221 c |= 0200; 222 bslashmode = FALSE; 223 } 224 else if (c == '\\') 225 { 226 bslashmode = TRUE; 227 continue; 228 } 229 230 nstate = toktype(c); 231 switch (state) 232 { 233 case QSTRING: /* in quoted string */ 234 if (c == '"') 235 state = OPER; 236 break; 237 238 case ATOM: /* regular atom */ 239 state = nstate; 240 if (state != ATOM) 241 { 242 state = EOTOK; 243 p--; 244 } 245 break; 246 247 case GETONE: /* grab one character */ 248 state = OPER; 249 break; 250 251 case EOTOK: /* after atom or q-string */ 252 state = nstate; 253 if (state == SPACE) 254 continue; 255 break; 256 257 case SPACE: /* linear white space */ 258 state = nstate; 259 space = TRUE; 260 continue; 261 262 case OPER: /* operator */ 263 if (nstate == SPACE) 264 continue; 265 state = nstate; 266 break; 267 268 case DOLLAR: /* $- etc. */ 269 state = OPER; 270 switch (c) 271 { 272 case '$': /* literal $ */ 273 break; 274 275 case '+': /* match anything */ 276 c = MATCHANY; 277 state = GETONE; 278 break; 279 280 case '-': /* match one token */ 281 c = MATCHONE; 282 state = GETONE; 283 break; 284 285 case '=': /* match one token of class */ 286 c = MATCHCLASS; 287 state = GETONE; 288 break; 289 290 case '#': /* canonical net name */ 291 c = CANONNET; 292 break; 293 294 case '@': /* canonical host name */ 295 c = CANONHOST; 296 break; 297 298 case ':': /* canonical user name */ 299 c = CANONUSER; 300 break; 301 302 default: 303 c = '$'; 304 state = OPER; 305 p--; 306 break; 307 } 308 break; 309 310 default: 311 syserr("prescan: unknown state %d", state); 312 } 313 314 if (state == OPER) 315 space = FALSE; 316 else if (state == EOTOK) 317 break; 318 if (c == '$' && delim == '\t') 319 { 320 state = DOLLAR; 321 continue; 322 } 323 324 /* squirrel it away */ 325 if (q >= &buf[sizeof buf - 5]) 326 { 327 usrerr("Address too long"); 328 return (NULL); 329 } 330 if (space) 331 *q++ = SPACESUB; 332 *q++ = c; 333 334 /* decide whether this represents end of token */ 335 if (state == OPER) 336 break; 337 } 338 if (c == '\0' || c == delim) 339 p--; 340 341 /* new token */ 342 if (tok == q) 343 continue; 344 *q++ = '\0'; 345 346 c = tok[0]; 347 if (c == '(') 348 { 349 cmntcnt++; 350 continue; 351 } 352 else if (c == ')') 353 { 354 if (cmntcnt <= 0) 355 { 356 usrerr("Unbalanced ')'"); 357 return (NULL); 358 } 359 else 360 { 361 cmntcnt--; 362 continue; 363 } 364 } 365 else if (cmntcnt > 0) 366 continue; 367 368 *avp++ = tok; 369 370 /* we prefer <> specs */ 371 if (c == '<') 372 { 373 if (brccnt < 0) 374 { 375 usrerr("multiple < spec"); 376 return (NULL); 377 } 378 brccnt++; 379 space = FALSE; 380 if (brccnt == 1) 381 { 382 /* we prefer using machine readable name */ 383 q = buf; 384 *q = '\0'; 385 avp = av; 386 continue; 387 } 388 } 389 else if (c == '>') 390 { 391 if (brccnt <= 0) 392 { 393 usrerr("Unbalanced `>'"); 394 return (NULL); 395 } 396 else 397 brccnt--; 398 if (brccnt <= 0) 399 { 400 brccnt = -1; 401 continue; 402 } 403 } 404 405 /* 406 ** Turn "at" into "@", 407 ** but only if "at" is a word. 408 */ 409 410 if (lower(tok[0]) == 'a' && lower(tok[1]) == 't' && tok[2] == '\0') 411 { 412 tok[0] = '@'; 413 tok[1] = '\0'; 414 } 415 } 416 *avp = NULL; 417 if (cmntcnt > 0) 418 usrerr("Unbalanced '('"); 419 else if (brccnt > 0) 420 usrerr("Unbalanced '<'"); 421 else if (state == QSTRING) 422 usrerr("Unbalanced '\"'"); 423 else if (av[0] != NULL) 424 return (av); 425 return (NULL); 426 } 427 /* 428 ** TOKTYPE -- return token type 429 ** 430 ** Parameters: 431 ** c -- the character in question. 432 ** 433 ** Returns: 434 ** Its type. 435 ** 436 ** Side Effects: 437 ** none. 438 */ 439 440 toktype(c) 441 register char c; 442 { 443 static char buf[50]; 444 static bool firstime = TRUE; 445 446 if (firstime) 447 { 448 firstime = FALSE; 449 expand("$o", buf, &buf[sizeof buf - 1]); 450 strcat(buf, DELIMCHARS); 451 } 452 if (isspace(c)) 453 return (SPACE); 454 if (iscntrl(c) || index(buf, c) != NULL) 455 return (OPER); 456 return (ATOM); 457 } 458 /* 459 ** REWRITE -- apply rewrite rules to token vector. 460 ** 461 ** Parameters: 462 ** pvp -- pointer to token vector. 463 ** 464 ** Returns: 465 ** none. 466 ** 467 ** Side Effects: 468 ** pvp is modified. 469 */ 470 471 struct match 472 { 473 char **firsttok; /* first token matched */ 474 char **lasttok; /* last token matched */ 475 char name; /* name of parameter */ 476 }; 477 478 # define MAXMATCH 8 /* max params per rewrite */ 479 480 481 rewrite(pvp) 482 char **pvp; 483 { 484 register char *ap; /* address pointer */ 485 register char *rp; /* rewrite pointer */ 486 register char **avp; /* address vector pointer */ 487 register char **rvp; /* rewrite vector pointer */ 488 struct rewrite *rwr; 489 struct match mlist[MAXMATCH]; 490 char *npvp[MAXATOM+1]; /* temporary space for rebuild */ 491 extern bool sameword(); 492 493 # ifdef DEBUGX 494 if (Debug) 495 { 496 printf("rewrite: original pvp:\n"); 497 printav(pvp); 498 } 499 # endif DEBUGX 500 501 /* 502 ** Run through the list of rewrite rules, applying 503 ** any that match. 504 */ 505 506 for (rwr = RewriteRules; rwr != NULL; ) 507 { 508 # ifdef DEBUGX 509 if (Debug) 510 { 511 printf("-----trying rule:\n"); 512 printav(rwr->r_lhs); 513 } 514 # endif DEBUGX 515 516 /* try to match on this rule */ 517 clrmatch(mlist); 518 for (rvp = rwr->r_lhs, avp = pvp; *avp != NULL; ) 519 { 520 ap = *avp; 521 rp = *rvp; 522 523 if (rp == NULL) 524 { 525 /* end-of-pattern before end-of-address */ 526 goto fail; 527 } 528 529 switch (*rp) 530 { 531 register STAB *s; 532 register int class; 533 534 case MATCHONE: 535 /* match exactly one token */ 536 setmatch(mlist, rp[1], avp, avp); 537 break; 538 539 case MATCHANY: 540 /* match any number of tokens */ 541 setmatch(mlist, rp[1], NULL, avp); 542 break; 543 544 case MATCHCLASS: 545 /* match any token in a class */ 546 class = rp[1]; 547 if (!isalpha(class)) 548 goto fail; 549 if (isupper(class)) 550 class -= 'A'; 551 else 552 class -= 'a'; 553 s = stab(ap, ST_FIND); 554 if (s == NULL || (s->s_class & (1 << class)) == 0) 555 goto fail; 556 break; 557 558 default: 559 /* must have exact match */ 560 if (!sameword(rp, ap)) 561 goto fail; 562 break; 563 } 564 565 /* successful match on this token */ 566 avp++; 567 rvp++; 568 continue; 569 570 fail: 571 /* match failed -- back up */ 572 while (--rvp >= rwr->r_lhs) 573 { 574 rp = *rvp; 575 if (*rp == MATCHANY) 576 break; 577 578 /* can't extend match: back up everything */ 579 avp--; 580 581 if (*rp == MATCHONE) 582 { 583 /* undo binding */ 584 setmatch(mlist, rp[1], NULL, NULL); 585 } 586 } 587 588 if (rvp < rwr->r_lhs) 589 { 590 /* total failure to match */ 591 break; 592 } 593 } 594 595 /* 596 ** See if we successfully matched 597 */ 598 599 if (rvp >= rwr->r_lhs && *rvp == NULL) 600 { 601 # ifdef DEBUGX 602 if (Debug) 603 { 604 printf("-----rule matches:\n"); 605 printav(rwr->r_rhs); 606 } 607 # endif DEBUGX 608 609 /* substitute */ 610 for (rvp = rwr->r_rhs, avp = npvp; *rvp != NULL; rvp++) 611 { 612 rp = *rvp; 613 if (*rp == MATCHANY) 614 { 615 register struct match *m; 616 register char **pp; 617 extern struct match *findmatch(); 618 619 m = findmatch(mlist, rp[1]); 620 if (m != NULL) 621 { 622 pp = m->firsttok; 623 do 624 { 625 *avp++ = *pp; 626 } while (pp++ != m->lasttok); 627 } 628 } 629 else 630 *avp++ = rp; 631 } 632 *avp++ = NULL; 633 bmove(npvp, pvp, (avp - npvp) * sizeof *avp); 634 # ifdef DEBUG 635 if (Debug) 636 { 637 char **vp; 638 639 printf("rewritten as `"); 640 for (vp = pvp; *vp != NULL; vp++) 641 xputs(*vp); 642 printf("'\n"); 643 } 644 # endif DEBUG 645 if (pvp[0][0] == CANONNET) 646 break; 647 } 648 else 649 { 650 # ifdef DEBUGX 651 if (Debug) 652 printf("----- rule fails\n"); 653 # endif DEBUGX 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 extern char *xalloc(); 790 791 if (a == NULL) 792 a = (ADDRESS *) xalloc(sizeof *a); 793 a->q_flags = 0; 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_NOHOST, 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 buf[0] = '\0'; 825 while (**++tv != NULL) 826 strcat(buf, *tv); 827 a->q_user = buf; 828 829 return (a); 830 } 831 /* 832 ** SAMEADDR -- Determine if two addresses are the same 833 ** 834 ** This is not just a straight comparison -- if the mailer doesn't 835 ** care about the host we just ignore it, etc. 836 ** 837 ** Parameters: 838 ** a, b -- pointers to the internal forms to compare. 839 ** wildflg -- if TRUE, 'a' may have no user specified, 840 ** in which case it is to match anything. 841 ** 842 ** Returns: 843 ** TRUE -- they represent the same mailbox. 844 ** FALSE -- they don't. 845 ** 846 ** Side Effects: 847 ** none. 848 */ 849 850 bool 851 sameaddr(a, b, wildflg) 852 register ADDRESS *a; 853 register ADDRESS *b; 854 bool wildflg; 855 { 856 /* if they don't have the same mailer, forget it */ 857 if (a->q_mailer != b->q_mailer) 858 return (FALSE); 859 860 /* if the user isn't the same, we can drop out */ 861 if ((!wildflg || a->q_user[0] != '\0') && strcmp(a->q_user, b->q_user) != 0) 862 return (FALSE); 863 864 /* if the mailer ignores hosts, we have succeeded! */ 865 if (bitset(M_NOHOST, Mailer[a->q_mailer]->m_flags)) 866 return (TRUE); 867 868 /* otherwise compare hosts (but be careful for NULL ptrs) */ 869 if (a->q_host == NULL || b->q_host == NULL) 870 return (FALSE); 871 if (strcmp(a->q_host, b->q_host) != 0) 872 return (FALSE); 873 874 return (TRUE); 875 } 876 /* 877 ** PRINTADDR -- print address (for debugging) 878 ** 879 ** Parameters: 880 ** a -- the address to print 881 ** follow -- follow the q_next chain. 882 ** 883 ** Returns: 884 ** none. 885 ** 886 ** Side Effects: 887 ** none. 888 */ 889 890 printaddr(a, follow) 891 register ADDRESS *a; 892 bool follow; 893 { 894 while (a != NULL) 895 { 896 printf("addr@%x: ", a); 897 fflush(stdout); 898 printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr, 899 a->q_mailer, Mailer[a->q_mailer]->m_name, a->q_host, a->q_user); 900 printf("\tnext=%x flags=%o, rmailer %d\n", a->q_next, 901 a->q_flags, a->q_rmailer); 902 903 if (!follow) 904 return; 905 a = a->q_next; 906 } 907 } 908