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