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