1 # include <stdio.h> 2 # include <ctype.h> 3 # include "postbox.h" 4 5 static char SccsId[] = "@(#)parseaddr.c 3.9 03/12/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 "$()<>@!.,;:\\\" \t\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 '#': /* canonical net name */ 286 c = CANONNET; 287 break; 288 289 case '@': /* canonical host name */ 290 c = CANONHOST; 291 break; 292 293 case ':': /* canonical user name */ 294 c = CANONUSER; 295 break; 296 297 default: 298 c = '$'; 299 state = OPER; 300 p--; 301 break; 302 } 303 break; 304 305 default: 306 syserr("prescan: unknown state %d", state); 307 } 308 309 if (state == OPER) 310 space = FALSE; 311 else if (state == EOTOK) 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 if (space) 326 *q++ = SPACESUB; 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 /* 401 ** Turn "at" into "@", 402 ** but only if "at" is a word. 403 */ 404 405 if (lower(tok[0]) == 'a' && lower(tok[1]) == 't' && tok[2] == '\0') 406 { 407 tok[0] = '@'; 408 tok[1] = '\0'; 409 } 410 } 411 *avp = NULL; 412 if (cmntcnt > 0) 413 usrerr("Unbalanced '('"); 414 else if (brccnt > 0) 415 usrerr("Unbalanced '<'"); 416 else if (state == QSTRING) 417 usrerr("Unbalanced '\"'"); 418 else if (av[0] != NULL) 419 return (av); 420 return (NULL); 421 } 422 /* 423 ** TOKTYPE -- return token type 424 ** 425 ** Parameters: 426 ** c -- the character in question. 427 ** 428 ** Returns: 429 ** Its type. 430 ** 431 ** Side Effects: 432 ** none. 433 */ 434 435 toktype(c) 436 register char c; 437 { 438 if (isspace(c)) 439 return (SPACE); 440 if (index(DELIMCHARS, c) != NULL || iscntrl(c)) 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) 468 char **pvp; 469 { 470 register char *ap; /* address pointer */ 471 register char *rp; /* rewrite pointer */ 472 register char **avp; /* address vector pointer */ 473 register char **rvp; /* rewrite vector pointer */ 474 struct rewrite *rwr; 475 struct match mlist[MAXMATCH]; 476 char *npvp[MAXATOM+1]; /* temporary space for rebuild */ 477 478 # ifdef DEBUGX 479 if (Debug) 480 { 481 printf("rewrite: original pvp:\n"); 482 printav(pvp); 483 } 484 # endif DEBUGX 485 486 /* 487 ** Run through the list of rewrite rules, applying 488 ** any that match. 489 */ 490 491 for (rwr = RewriteRules; rwr != NULL; ) 492 { 493 # ifdef DEBUGX 494 if (Debug) 495 { 496 printf("-----trying rule:\n"); 497 printav(rwr->r_lhs); 498 } 499 # endif DEBUGX 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 case MATCHONE: 517 /* match exactly one token */ 518 setmatch(mlist, rp[1], avp, avp); 519 break; 520 521 case MATCHANY: 522 /* match any number of tokens */ 523 setmatch(mlist, rp[1], NULL, avp); 524 break; 525 526 default: 527 /* must have exact match */ 528 /* can scribble rp & ap here safely */ 529 while (*rp != '\0' || *ap != '\0') 530 { 531 if (*rp++ != lower(*ap++)) 532 goto fail; 533 } 534 break; 535 } 536 537 /* successful match on this token */ 538 avp++; 539 rvp++; 540 continue; 541 542 fail: 543 /* match failed -- back up */ 544 while (--rvp >= rwr->r_lhs) 545 { 546 rp = *rvp; 547 if (*rp == MATCHANY) 548 break; 549 550 /* can't extend match: back up everything */ 551 avp--; 552 553 if (*rp == MATCHONE) 554 { 555 /* undo binding */ 556 setmatch(mlist, rp[1], NULL, NULL); 557 } 558 } 559 560 if (rvp < rwr->r_lhs) 561 { 562 /* total failure to match */ 563 break; 564 } 565 } 566 567 /* 568 ** See if we successfully matched 569 */ 570 571 if (rvp >= rwr->r_lhs && *rvp == NULL) 572 { 573 # ifdef DEBUGX 574 if (Debug) 575 { 576 printf("-----rule matches:\n"); 577 printav(rwr->r_rhs); 578 } 579 # endif DEBUGX 580 581 /* substitute */ 582 for (rvp = rwr->r_rhs, avp = npvp; *rvp != NULL; rvp++) 583 { 584 rp = *rvp; 585 if (*rp == MATCHANY) 586 { 587 register struct match *m; 588 register char **pp; 589 extern struct match *findmatch(); 590 591 m = findmatch(mlist, rp[1]); 592 if (m != NULL) 593 { 594 pp = m->firsttok; 595 do 596 { 597 *avp++ = *pp; 598 } while (pp++ != m->lasttok); 599 } 600 } 601 else 602 *avp++ = rp; 603 } 604 *avp++ = NULL; 605 bmove(npvp, pvp, (avp - npvp) * sizeof *avp); 606 # ifdef DEBUG 607 if (Debug) 608 { 609 char **vp; 610 611 printf("rewritten as `"); 612 for (vp = pvp; *vp != NULL; vp++) 613 xputs(*vp); 614 printf("'\n"); 615 } 616 # endif DEBUG 617 if (pvp[0][0] == CANONNET) 618 break; 619 } 620 else 621 { 622 # ifdef DEBUGX 623 if (Debug) 624 printf("----- rule fails\n"); 625 # endif DEBUGX 626 rwr = rwr->r_next; 627 } 628 } 629 } 630 /* 631 ** SETMATCH -- set parameter value in match vector 632 ** 633 ** Parameters: 634 ** mlist -- list of match values. 635 ** name -- the character name of this parameter. 636 ** first -- the first location of the replacement. 637 ** last -- the last location of the replacement. 638 ** 639 ** If last == NULL, delete this entry. 640 ** If first == NULL, extend this entry (or add it if 641 ** it does not exist). 642 ** 643 ** Returns: 644 ** nothing. 645 ** 646 ** Side Effects: 647 ** munges with mlist. 648 */ 649 650 setmatch(mlist, name, first, last) 651 struct match *mlist; 652 char name; 653 char **first; 654 char **last; 655 { 656 register struct match *m; 657 struct match *nullm = NULL; 658 659 for (m = mlist; m < &mlist[MAXMATCH]; m++) 660 { 661 if (m->name == name) 662 break; 663 if (m->name == '\0') 664 nullm = m; 665 } 666 667 if (m >= &mlist[MAXMATCH]) 668 m = nullm; 669 670 if (last == NULL) 671 { 672 m->name = '\0'; 673 return; 674 } 675 676 if (m->name == '\0') 677 { 678 if (first == NULL) 679 m->firsttok = last; 680 else 681 m->firsttok = first; 682 } 683 m->name = name; 684 m->lasttok = last; 685 } 686 /* 687 ** FINDMATCH -- find match in mlist 688 ** 689 ** Parameters: 690 ** mlist -- list to search. 691 ** name -- name to find. 692 ** 693 ** Returns: 694 ** pointer to match structure. 695 ** NULL if no match. 696 ** 697 ** Side Effects: 698 ** none. 699 */ 700 701 struct match * 702 findmatch(mlist, name) 703 struct match *mlist; 704 char name; 705 { 706 register struct match *m; 707 708 for (m = mlist; m < &mlist[MAXMATCH]; m++) 709 { 710 if (m->name == name) 711 return (m); 712 } 713 714 return (NULL); 715 } 716 /* 717 ** CLRMATCH -- clear match list 718 ** 719 ** Parameters: 720 ** mlist -- list to clear. 721 ** 722 ** Returns: 723 ** none. 724 ** 725 ** Side Effects: 726 ** mlist is cleared. 727 */ 728 729 clrmatch(mlist) 730 struct match *mlist; 731 { 732 register struct match *m; 733 734 for (m = mlist; m < &mlist[MAXMATCH]; m++) 735 m->name = '\0'; 736 } 737 /* 738 ** BUILDADDR -- build address from token vector. 739 ** 740 ** Parameters: 741 ** tv -- token vector. 742 ** a -- pointer to address descriptor to fill. 743 ** If NULL, one will be allocated. 744 ** 745 ** Returns: 746 ** 'a' 747 ** 748 ** Side Effects: 749 ** fills in 'a' 750 */ 751 752 ADDRESS * 753 buildaddr(tv, a) 754 register char **tv; 755 register ADDRESS *a; 756 { 757 register int i; 758 static char buf[MAXNAME]; 759 struct mailer **mp; 760 register struct mailer *m; 761 extern char *xalloc(); 762 763 if (a == NULL) 764 a = (ADDRESS *) xalloc(sizeof *a); 765 a->q_flags = 0; 766 767 /* figure out what net/mailer to use */ 768 if (**tv != CANONNET) 769 syserr("buildaddr: no net"); 770 tv++; 771 for (mp = Mailer, i = 0; (m = *mp++) != NULL; i++) 772 { 773 if (strcmp(m->m_name, *tv) == 0) 774 break; 775 } 776 if (m == NULL) 777 syserr("buildaddr: unknown net %s", *tv); 778 a->q_mailer = i; 779 780 /* figure out what host (if any) */ 781 tv++; 782 if (!bitset(M_NOHOST, m->m_flags)) 783 { 784 if (**tv != CANONHOST) 785 syserr("buildaddr: no host"); 786 tv++; 787 a->q_host = *tv; 788 tv++; 789 } 790 else 791 a->q_host = NULL; 792 793 /* figure out the user */ 794 if (**tv != CANONUSER) 795 syserr("buildaddr: no user"); 796 buf[0] = '\0'; 797 while (**++tv != NULL) 798 strcat(buf, *tv); 799 a->q_user = buf; 800 801 return (a); 802 } 803 /* 804 ** SAMEADDR -- Determine if two addresses are the same 805 ** 806 ** This is not just a straight comparison -- if the mailer doesn't 807 ** care about the host we just ignore it, etc. 808 ** 809 ** Parameters: 810 ** a, b -- pointers to the internal forms to compare. 811 ** wildflg -- if TRUE, 'a' may have no user specified, 812 ** in which case it is to match anything. 813 ** 814 ** Returns: 815 ** TRUE -- they represent the same mailbox. 816 ** FALSE -- they don't. 817 ** 818 ** Side Effects: 819 ** none. 820 */ 821 822 bool 823 sameaddr(a, b, wildflg) 824 register ADDRESS *a; 825 register ADDRESS *b; 826 bool wildflg; 827 { 828 /* if they don't have the same mailer, forget it */ 829 if (a->q_mailer != b->q_mailer) 830 return (FALSE); 831 832 /* if the user isn't the same, we can drop out */ 833 if ((!wildflg || a->q_user[0] != '\0') && strcmp(a->q_user, b->q_user) != 0) 834 return (FALSE); 835 836 /* if the mailer ignores hosts, we have succeeded! */ 837 if (bitset(M_NOHOST, Mailer[a->q_mailer]->m_flags)) 838 return (TRUE); 839 840 /* otherwise compare hosts (but be careful for NULL ptrs) */ 841 if (a->q_host == NULL || b->q_host == NULL) 842 return (FALSE); 843 if (strcmp(a->q_host, b->q_host) != 0) 844 return (FALSE); 845 846 return (TRUE); 847 } 848 /* 849 ** PRINTADDR -- print address (for debugging) 850 ** 851 ** Parameters: 852 ** a -- the address to print 853 ** follow -- follow the q_next chain. 854 ** 855 ** Returns: 856 ** none. 857 ** 858 ** Side Effects: 859 ** none. 860 */ 861 862 printaddr(a, follow) 863 register ADDRESS *a; 864 bool follow; 865 { 866 while (a != NULL) 867 { 868 printf("addr@%x: ", a); 869 fflush(stdout); 870 printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr, 871 a->q_mailer, Mailer[a->q_mailer]->m_name, a->q_host, a->q_user); 872 printf("\tnext=%x flags=%o, rmailer %d\n", a->q_next, 873 a->q_flags, a->q_rmailer); 874 875 if (!follow) 876 return; 877 a = a->q_next; 878 } 879 } 880