1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)parseaddr.c 8.8 (Berkeley) 08/19/93"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 15 /* 16 ** PARSEADDR -- Parse an address 17 ** 18 ** Parses an address and breaks it up into three parts: a 19 ** net to transmit the message on, the host to transmit it 20 ** to, and a user on that host. These are loaded into an 21 ** ADDRESS header with the values squirreled away if necessary. 22 ** The "user" part may not be a real user; the process may 23 ** just reoccur on that machine. For example, on a machine 24 ** with an arpanet connection, the address 25 ** csvax.bill@berkeley 26 ** will break up to a "user" of 'csvax.bill' and a host 27 ** of 'berkeley' -- to be transmitted over the arpanet. 28 ** 29 ** Parameters: 30 ** addr -- the address to parse. 31 ** a -- a pointer to the address descriptor buffer. 32 ** If NULL, a header will be created. 33 ** flags -- describe detail for parsing. See RF_ definitions 34 ** in sendmail.h. 35 ** delim -- the character to terminate the address, passed 36 ** to prescan. 37 ** delimptr -- if non-NULL, set to the location of the 38 ** delim character that was found. 39 ** e -- the envelope that will contain this address. 40 ** 41 ** Returns: 42 ** A pointer to the address descriptor header (`a' if 43 ** `a' is non-NULL). 44 ** NULL on error. 45 ** 46 ** Side Effects: 47 ** none 48 */ 49 50 /* following delimiters are inherent to the internal algorithms */ 51 # define DELIMCHARS "()<>,;\r\n" /* default word delimiters */ 52 53 ADDRESS * 54 parseaddr(addr, a, flags, delim, delimptr, e) 55 char *addr; 56 register ADDRESS *a; 57 int flags; 58 int delim; 59 char **delimptr; 60 register ENVELOPE *e; 61 { 62 register char **pvp; 63 auto char *delimptrbuf; 64 bool queueup; 65 char pvpbuf[PSBUFSIZE]; 66 extern ADDRESS *buildaddr(); 67 extern bool invalidaddr(); 68 69 /* 70 ** Initialize and prescan address. 71 */ 72 73 e->e_to = addr; 74 if (tTd(20, 1)) 75 printf("\n--parseaddr(%s)\n", addr); 76 77 if (invalidaddr(addr)) 78 { 79 if (tTd(20, 1)) 80 printf("parseaddr-->bad address\n"); 81 return NULL; 82 } 83 84 if (delimptr == NULL) 85 delimptr = &delimptrbuf; 86 87 pvp = prescan(addr, delim, pvpbuf, delimptr); 88 if (pvp == NULL) 89 { 90 if (tTd(20, 1)) 91 printf("parseaddr-->NULL\n"); 92 return (NULL); 93 } 94 95 /* 96 ** Apply rewriting rules. 97 ** Ruleset 0 does basic parsing. It must resolve. 98 */ 99 100 queueup = FALSE; 101 if (rewrite(pvp, 3, e) == EX_TEMPFAIL) 102 queueup = TRUE; 103 if (rewrite(pvp, 0, e) == EX_TEMPFAIL) 104 queueup = TRUE; 105 106 107 /* 108 ** Build canonical address from pvp. 109 */ 110 111 a = buildaddr(pvp, a, flags, e); 112 113 /* 114 ** Make local copies of the host & user and then 115 ** transport them out. 116 */ 117 118 allocaddr(a, flags, addr, *delimptr); 119 if (bitset(QBADADDR, a->q_flags)) 120 return a; 121 122 /* 123 ** If there was a parsing failure, mark it for queueing. 124 */ 125 126 if (queueup) 127 { 128 char *msg = "Transient parse error -- message queued for future delivery"; 129 130 if (tTd(20, 1)) 131 printf("parseaddr: queuing message\n"); 132 message(msg); 133 if (e->e_message == NULL) 134 e->e_message = newstr(msg); 135 a->q_flags |= QQUEUEUP; 136 } 137 138 /* 139 ** Compute return value. 140 */ 141 142 if (tTd(20, 1)) 143 { 144 printf("parseaddr-->"); 145 printaddr(a, FALSE); 146 } 147 148 return (a); 149 } 150 /* 151 ** INVALIDADDR -- check for address containing meta-characters 152 ** 153 ** Parameters: 154 ** addr -- the address to check. 155 ** 156 ** Returns: 157 ** TRUE -- if the address has any "wierd" characters 158 ** FALSE -- otherwise. 159 */ 160 161 bool 162 invalidaddr(addr) 163 register char *addr; 164 { 165 for (; *addr != '\0'; addr++) 166 { 167 if ((*addr & 0340) != 0200) 168 continue; 169 setstat(EX_USAGE); 170 usrerr("553 Address contained invalid control characters"); 171 return TRUE; 172 } 173 return FALSE; 174 } 175 /* 176 ** ALLOCADDR -- do local allocations of address on demand. 177 ** 178 ** Also lowercases the host name if requested. 179 ** 180 ** Parameters: 181 ** a -- the address to reallocate. 182 ** flags -- the copy flag (see RF_ definitions in sendmail.h 183 ** for a description). 184 ** paddr -- the printname of the address. 185 ** delimptr -- a pointer to the address delimiter. Must be set. 186 ** 187 ** Returns: 188 ** none. 189 ** 190 ** Side Effects: 191 ** Copies portions of a into local buffers as requested. 192 */ 193 194 allocaddr(a, flags, paddr, delimptr) 195 register ADDRESS *a; 196 int flags; 197 char *paddr; 198 char *delimptr; 199 { 200 if (tTd(24, 4)) 201 printf("allocaddr(flags=%o, paddr=%s)\n", flags, paddr); 202 203 if (bitset(RF_COPYPADDR, flags) && paddr != NULL) 204 { 205 char savec = *delimptr; 206 207 if (savec != '\0') 208 *delimptr = '\0'; 209 a->q_paddr = newstr(paddr); 210 if (savec != '\0') 211 *delimptr = savec; 212 } 213 else 214 a->q_paddr = paddr; 215 216 if (a->q_user == NULL) 217 a->q_user = ""; 218 if (a->q_host == NULL) 219 a->q_host = ""; 220 221 if (bitset(RF_COPYPARSE, flags)) 222 { 223 a->q_host = newstr(a->q_host); 224 if (a->q_user != a->q_paddr) 225 a->q_user = newstr(a->q_user); 226 } 227 228 if (a->q_paddr == NULL) 229 a->q_paddr = a->q_user; 230 } 231 /* 232 ** PRESCAN -- Prescan name and make it canonical 233 ** 234 ** Scans a name and turns it into a set of tokens. This process 235 ** deletes blanks and comments (in parentheses). 236 ** 237 ** This routine knows about quoted strings and angle brackets. 238 ** 239 ** There are certain subtleties to this routine. The one that 240 ** comes to mind now is that backslashes on the ends of names 241 ** are silently stripped off; this is intentional. The problem 242 ** is that some versions of sndmsg (like at LBL) set the kill 243 ** character to something other than @ when reading addresses; 244 ** so people type "csvax.eric\@berkeley" -- which screws up the 245 ** berknet mailer. 246 ** 247 ** Parameters: 248 ** addr -- the name to chomp. 249 ** delim -- the delimiter for the address, normally 250 ** '\0' or ','; \0 is accepted in any case. 251 ** If '\t' then we are reading the .cf file. 252 ** pvpbuf -- place to put the saved text -- note that 253 ** the pointers are static. 254 ** delimptr -- if non-NULL, set to the location of the 255 ** terminating delimiter. 256 ** 257 ** Returns: 258 ** A pointer to a vector of tokens. 259 ** NULL on error. 260 */ 261 262 /* states and character types */ 263 # define OPR 0 /* operator */ 264 # define ATM 1 /* atom */ 265 # define QST 2 /* in quoted string */ 266 # define SPC 3 /* chewing up spaces */ 267 # define ONE 4 /* pick up one character */ 268 269 # define NSTATES 5 /* number of states */ 270 # define TYPE 017 /* mask to select state type */ 271 272 /* meta bits for table */ 273 # define M 020 /* meta character; don't pass through */ 274 # define B 040 /* cause a break */ 275 # define MB M|B /* meta-break */ 276 277 static short StateTab[NSTATES][NSTATES] = 278 { 279 /* oldst chtype> OPR ATM QST SPC ONE */ 280 /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, 281 /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B, 282 /*QST*/ QST, QST, OPR, QST, QST, 283 /*SPC*/ OPR, ATM, QST, SPC|M, ONE, 284 /*ONE*/ OPR, OPR, OPR, OPR, OPR, 285 }; 286 287 # define NOCHAR -1 /* signal nothing in lookahead token */ 288 289 char ** 290 prescan(addr, delim, pvpbuf, delimptr) 291 char *addr; 292 char delim; 293 char pvpbuf[]; 294 char **delimptr; 295 { 296 register char *p; 297 register char *q; 298 register int c; 299 char **avp; 300 bool bslashmode; 301 int cmntcnt; 302 int anglecnt; 303 char *tok; 304 int state; 305 int newstate; 306 char *saveto = CurEnv->e_to; 307 static char *av[MAXATOM+1]; 308 extern int errno; 309 310 /* make sure error messages don't have garbage on them */ 311 errno = 0; 312 313 q = pvpbuf; 314 bslashmode = FALSE; 315 cmntcnt = 0; 316 anglecnt = 0; 317 avp = av; 318 state = ATM; 319 c = NOCHAR; 320 p = addr; 321 CurEnv->e_to = p; 322 if (tTd(22, 11)) 323 { 324 printf("prescan: "); 325 xputs(p); 326 (void) putchar('\n'); 327 } 328 329 do 330 { 331 /* read a token */ 332 tok = q; 333 for (;;) 334 { 335 /* store away any old lookahead character */ 336 if (c != NOCHAR && !bslashmode) 337 { 338 /* see if there is room */ 339 if (q >= &pvpbuf[PSBUFSIZE - 5]) 340 { 341 usrerr("553 Address too long"); 342 if (delimptr != NULL) 343 *delimptr = p; 344 CurEnv->e_to = saveto; 345 return (NULL); 346 } 347 348 /* squirrel it away */ 349 *q++ = c; 350 } 351 352 /* read a new input character */ 353 c = *p++; 354 if (c == '\0') 355 { 356 /* diagnose and patch up bad syntax */ 357 if (state == QST) 358 { 359 usrerr("653 Unbalanced '\"' (fixed)"); 360 c = '"'; 361 } 362 else if (cmntcnt > 0) 363 { 364 usrerr("653 Unbalanced '(' (fixed)"); 365 c = ')'; 366 } 367 else if (anglecnt > 0) 368 { 369 c = '>'; 370 usrerr("653 Unbalanced '<' (fixed)"); 371 } 372 else 373 break; 374 375 p--; 376 } 377 else if (c == delim && anglecnt <= 0 && 378 cmntcnt <= 0 && state != QST) 379 break; 380 381 if (tTd(22, 101)) 382 printf("c=%c, s=%d; ", c, state); 383 384 /* chew up special characters */ 385 *q = '\0'; 386 if (bslashmode) 387 { 388 bslashmode = FALSE; 389 390 /* kludge \! for naive users */ 391 if (cmntcnt > 0) 392 { 393 c = NOCHAR; 394 continue; 395 } 396 else if (c != '!' || state == QST) 397 { 398 *q++ = '\\'; 399 continue; 400 } 401 } 402 403 if (c == '\\') 404 { 405 bslashmode = TRUE; 406 } 407 else if (state == QST) 408 { 409 /* do nothing, just avoid next clauses */ 410 } 411 else if (c == '(') 412 { 413 cmntcnt++; 414 c = NOCHAR; 415 } 416 else if (c == ')') 417 { 418 if (cmntcnt <= 0) 419 { 420 usrerr("653 Unbalanced ')' (fixed)"); 421 c = NOCHAR; 422 } 423 else 424 cmntcnt--; 425 } 426 else if (cmntcnt > 0) 427 c = NOCHAR; 428 else if (c == '<') 429 anglecnt++; 430 else if (c == '>') 431 { 432 if (anglecnt <= 0) 433 { 434 usrerr("653 Unbalanced '>' (fixed)"); 435 c = NOCHAR; 436 } 437 else 438 anglecnt--; 439 } 440 else if (delim == ' ' && isascii(c) && isspace(c)) 441 c = ' '; 442 443 if (c == NOCHAR) 444 continue; 445 446 /* see if this is end of input */ 447 if (c == delim && anglecnt <= 0 && state != QST) 448 break; 449 450 newstate = StateTab[state][toktype(c)]; 451 if (tTd(22, 101)) 452 printf("ns=%02o\n", newstate); 453 state = newstate & TYPE; 454 if (bitset(M, newstate)) 455 c = NOCHAR; 456 if (bitset(B, newstate)) 457 break; 458 } 459 460 /* new token */ 461 if (tok != q) 462 { 463 *q++ = '\0'; 464 if (tTd(22, 36)) 465 { 466 printf("tok="); 467 xputs(tok); 468 (void) putchar('\n'); 469 } 470 if (avp >= &av[MAXATOM]) 471 { 472 syserr("553 prescan: too many tokens"); 473 if (delimptr != NULL) 474 *delimptr = p; 475 CurEnv->e_to = saveto; 476 return (NULL); 477 } 478 *avp++ = tok; 479 } 480 } while (c != '\0' && (c != delim || anglecnt > 0)); 481 *avp = NULL; 482 p--; 483 if (delimptr != NULL) 484 *delimptr = p; 485 if (tTd(22, 12)) 486 { 487 printf("prescan==>"); 488 printav(av); 489 } 490 CurEnv->e_to = saveto; 491 if (av[0] == NULL) 492 return (NULL); 493 return (av); 494 } 495 /* 496 ** TOKTYPE -- return token type 497 ** 498 ** Parameters: 499 ** c -- the character in question. 500 ** 501 ** Returns: 502 ** Its type. 503 ** 504 ** Side Effects: 505 ** none. 506 */ 507 508 toktype(c) 509 register int c; 510 { 511 static char buf[50]; 512 static bool firstime = TRUE; 513 514 if (firstime) 515 { 516 firstime = FALSE; 517 expand("\201o", buf, &buf[sizeof buf - 1], CurEnv); 518 (void) strcat(buf, DELIMCHARS); 519 } 520 c &= 0377; 521 if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS) 522 return (ONE); 523 if (c == MACRODEXPAND) 524 return (ONE); 525 if (c == '"') 526 return (QST); 527 if ((c & 0340) == 0200) 528 return (OPR); 529 if (!isascii(c)) 530 return (ATM); 531 if (isspace(c) || c == ')') 532 return (SPC); 533 if (strchr(buf, c) != NULL) 534 return (OPR); 535 return (ATM); 536 } 537 /* 538 ** REWRITE -- apply rewrite rules to token vector. 539 ** 540 ** This routine is an ordered production system. Each rewrite 541 ** rule has a LHS (called the pattern) and a RHS (called the 542 ** rewrite); 'rwr' points the the current rewrite rule. 543 ** 544 ** For each rewrite rule, 'avp' points the address vector we 545 ** are trying to match against, and 'pvp' points to the pattern. 546 ** If pvp points to a special match value (MATCHZANY, MATCHANY, 547 ** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp 548 ** matched is saved away in the match vector (pointed to by 'mvp'). 549 ** 550 ** When a match between avp & pvp does not match, we try to 551 ** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS 552 ** we must also back out the match in mvp. If we reach a 553 ** MATCHANY or MATCHZANY we just extend the match and start 554 ** over again. 555 ** 556 ** When we finally match, we rewrite the address vector 557 ** and try over again. 558 ** 559 ** Parameters: 560 ** pvp -- pointer to token vector. 561 ** ruleset -- the ruleset to use for rewriting. 562 ** e -- the current envelope. 563 ** 564 ** Returns: 565 ** A status code. If EX_TEMPFAIL, higher level code should 566 ** attempt recovery. 567 ** 568 ** Side Effects: 569 ** pvp is modified. 570 */ 571 572 struct match 573 { 574 char **first; /* first token matched */ 575 char **last; /* last token matched */ 576 char **pattern; /* pointer to pattern */ 577 }; 578 579 # define MAXMATCH 9 /* max params per rewrite */ 580 581 582 int 583 rewrite(pvp, ruleset, e) 584 char **pvp; 585 int ruleset; 586 register ENVELOPE *e; 587 { 588 register char *ap; /* address pointer */ 589 register char *rp; /* rewrite pointer */ 590 register char **avp; /* address vector pointer */ 591 register char **rvp; /* rewrite vector pointer */ 592 register struct match *mlp; /* cur ptr into mlist */ 593 register struct rewrite *rwr; /* pointer to current rewrite rule */ 594 int ruleno; /* current rule number */ 595 int rstat = EX_OK; /* return status */ 596 struct match mlist[MAXMATCH]; /* stores match on LHS */ 597 char *npvp[MAXATOM+1]; /* temporary space for rebuild */ 598 599 if (OpMode == MD_TEST || tTd(21, 2)) 600 { 601 printf("rewrite: ruleset %2d input:", ruleset); 602 printav(pvp); 603 } 604 if (ruleset < 0 || ruleset >= MAXRWSETS) 605 { 606 syserr("554 rewrite: illegal ruleset number %d", ruleset); 607 return EX_CONFIG; 608 } 609 if (pvp == NULL) 610 return EX_USAGE; 611 612 /* 613 ** Run through the list of rewrite rules, applying 614 ** any that match. 615 */ 616 617 ruleno = 1; 618 for (rwr = RewriteRules[ruleset]; rwr != NULL; ) 619 { 620 int loopcount = 0; 621 622 if (tTd(21, 12)) 623 { 624 printf("-----trying rule:"); 625 printav(rwr->r_lhs); 626 } 627 628 /* try to match on this rule */ 629 mlp = mlist; 630 rvp = rwr->r_lhs; 631 avp = pvp; 632 if (++loopcount > 100) 633 { 634 syserr("554 Infinite loop in ruleset %d, rule %d", 635 ruleset, ruleno); 636 if (tTd(21, 1)) 637 { 638 printf("workspace: "); 639 printav(pvp); 640 } 641 break; 642 } 643 644 while ((ap = *avp) != NULL || *rvp != NULL) 645 { 646 rp = *rvp; 647 if (tTd(21, 35)) 648 { 649 printf("ADVANCE rp="); 650 xputs(rp); 651 printf(", ap="); 652 xputs(ap); 653 printf("\n"); 654 } 655 if (rp == NULL) 656 { 657 /* end-of-pattern before end-of-address */ 658 goto backup; 659 } 660 if (ap == NULL && (*rp & 0377) != MATCHZANY && 661 (*rp & 0377) != MATCHZERO) 662 { 663 /* end-of-input with patterns left */ 664 goto backup; 665 } 666 667 switch (*rp & 0377) 668 { 669 register STAB *s; 670 char buf[MAXLINE]; 671 672 case MATCHCLASS: 673 /* match any phrase in a class */ 674 mlp->pattern = rvp; 675 mlp->first = avp; 676 extendclass: 677 ap = *avp; 678 if (ap == NULL) 679 goto backup; 680 mlp->last = avp++; 681 cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0'); 682 s = stab(buf, ST_CLASS, ST_FIND); 683 if (s == NULL || !bitnset(rp[1], s->s_class)) 684 { 685 if (tTd(21, 36)) 686 { 687 printf("EXTEND rp="); 688 xputs(rp); 689 printf(", ap="); 690 xputs(ap); 691 printf("\n"); 692 } 693 goto extendclass; 694 } 695 if (tTd(21, 36)) 696 printf("CLMATCH\n"); 697 mlp++; 698 break; 699 700 case MATCHNCLASS: 701 /* match any token not in a class */ 702 s = stab(ap, ST_CLASS, ST_FIND); 703 if (s != NULL && bitnset(rp[1], s->s_class)) 704 goto backup; 705 706 /* fall through */ 707 708 case MATCHONE: 709 case MATCHANY: 710 /* match exactly one token */ 711 mlp->pattern = rvp; 712 mlp->first = avp; 713 mlp->last = avp++; 714 mlp++; 715 break; 716 717 case MATCHZANY: 718 /* match zero or more tokens */ 719 mlp->pattern = rvp; 720 mlp->first = avp; 721 mlp->last = avp - 1; 722 mlp++; 723 break; 724 725 case MATCHZERO: 726 /* match zero tokens */ 727 break; 728 729 case MACRODEXPAND: 730 /* 731 ** Match against run-time macro. 732 ** This algorithm is broken for the 733 ** general case (no recursive macros, 734 ** improper tokenization) but should 735 ** work for the usual cases. 736 */ 737 738 ap = macvalue(rp[1], e); 739 mlp->first = avp; 740 if (tTd(21, 2)) 741 printf("rewrite: LHS $&%c => \"%s\"\n", 742 rp[1], 743 ap == NULL ? "(NULL)" : ap); 744 745 if (ap == NULL) 746 break; 747 while (*ap != '\0') 748 { 749 if (*avp == NULL || 750 strncasecmp(ap, *avp, strlen(*avp)) != 0) 751 { 752 /* no match */ 753 avp = mlp->first; 754 goto backup; 755 } 756 ap += strlen(*avp++); 757 } 758 759 /* match */ 760 break; 761 762 default: 763 /* must have exact match */ 764 if (strcasecmp(rp, ap)) 765 goto backup; 766 avp++; 767 break; 768 } 769 770 /* successful match on this token */ 771 rvp++; 772 continue; 773 774 backup: 775 /* match failed -- back up */ 776 while (--mlp >= mlist) 777 { 778 rvp = mlp->pattern; 779 rp = *rvp; 780 avp = mlp->last + 1; 781 ap = *avp; 782 783 if (tTd(21, 36)) 784 { 785 printf("BACKUP rp="); 786 xputs(rp); 787 printf(", ap="); 788 xputs(ap); 789 printf("\n"); 790 } 791 792 if (ap == NULL) 793 { 794 /* run off the end -- back up again */ 795 continue; 796 } 797 if ((*rp & 0377) == MATCHANY || 798 (*rp & 0377) == MATCHZANY) 799 { 800 /* extend binding and continue */ 801 mlp->last = avp++; 802 rvp++; 803 mlp++; 804 break; 805 } 806 if ((*rp & 0377) == MATCHCLASS) 807 { 808 /* extend binding and try again */ 809 mlp->last = avp; 810 goto extendclass; 811 } 812 } 813 814 if (mlp < mlist) 815 { 816 /* total failure to match */ 817 break; 818 } 819 } 820 821 /* 822 ** See if we successfully matched 823 */ 824 825 if (mlp < mlist || *rvp != NULL) 826 { 827 if (tTd(21, 10)) 828 printf("----- rule fails\n"); 829 rwr = rwr->r_next; 830 ruleno++; 831 continue; 832 } 833 834 rvp = rwr->r_rhs; 835 if (tTd(21, 12)) 836 { 837 printf("-----rule matches:"); 838 printav(rvp); 839 } 840 841 rp = *rvp; 842 if ((*rp & 0377) == CANONUSER) 843 { 844 rvp++; 845 rwr = rwr->r_next; 846 ruleno++; 847 } 848 else if ((*rp & 0377) == CANONHOST) 849 { 850 rvp++; 851 rwr = NULL; 852 } 853 else if ((*rp & 0377) == CANONNET) 854 rwr = NULL; 855 856 /* substitute */ 857 for (avp = npvp; *rvp != NULL; rvp++) 858 { 859 register struct match *m; 860 register char **pp; 861 862 rp = *rvp; 863 if ((*rp & 0377) == MATCHREPL) 864 { 865 /* substitute from LHS */ 866 m = &mlist[rp[1] - '1']; 867 if (m < mlist || m >= mlp) 868 { 869 syserr("554 rewrite: ruleset %d: replacement $%c out of bounds", 870 ruleset, rp[1]); 871 return EX_CONFIG; 872 } 873 if (tTd(21, 15)) 874 { 875 printf("$%c:", rp[1]); 876 pp = m->first; 877 while (pp <= m->last) 878 { 879 printf(" %x=\"", *pp); 880 (void) fflush(stdout); 881 printf("%s\"", *pp++); 882 } 883 printf("\n"); 884 } 885 pp = m->first; 886 while (pp <= m->last) 887 { 888 if (avp >= &npvp[MAXATOM]) 889 { 890 syserr("554 rewrite: expansion too long"); 891 return EX_DATAERR; 892 } 893 *avp++ = *pp++; 894 } 895 } 896 else 897 { 898 /* vanilla replacement */ 899 if (avp >= &npvp[MAXATOM]) 900 { 901 toolong: 902 syserr("554 rewrite: expansion too long"); 903 return EX_DATAERR; 904 } 905 if ((*rp & 0377) != MACRODEXPAND) 906 *avp++ = rp; 907 else 908 { 909 *avp = macvalue(rp[1], e); 910 if (tTd(21, 2)) 911 printf("rewrite: RHS $&%c => \"%s\"\n", 912 rp[1], 913 *avp == NULL ? "(NULL)" : *avp); 914 if (*avp != NULL) 915 avp++; 916 } 917 } 918 } 919 *avp++ = NULL; 920 921 /* 922 ** Check for any hostname/keyword lookups. 923 */ 924 925 for (rvp = npvp; *rvp != NULL; rvp++) 926 { 927 char **hbrvp; 928 char **xpvp; 929 int trsize; 930 char *replac; 931 int endtoken; 932 STAB *map; 933 char *mapname; 934 char **key_rvp; 935 char **arg_rvp; 936 char **default_rvp; 937 char buf[MAXNAME + 1]; 938 char *pvpb1[MAXATOM + 1]; 939 char *argvect[10]; 940 char pvpbuf[PSBUFSIZE]; 941 942 if ((**rvp & 0377) != HOSTBEGIN && 943 (**rvp & 0377) != LOOKUPBEGIN) 944 continue; 945 946 /* 947 ** Got a hostname/keyword lookup. 948 ** 949 ** This could be optimized fairly easily. 950 */ 951 952 hbrvp = rvp; 953 if ((**rvp & 0377) == HOSTBEGIN) 954 { 955 endtoken = HOSTEND; 956 mapname = "host"; 957 } 958 else 959 { 960 endtoken = LOOKUPEND; 961 mapname = *++rvp; 962 } 963 map = stab(mapname, ST_MAP, ST_FIND); 964 if (map == NULL) 965 syserr("554 rewrite: map %s not found", mapname); 966 967 /* extract the match part */ 968 key_rvp = ++rvp; 969 default_rvp = NULL; 970 arg_rvp = argvect; 971 xpvp = NULL; 972 replac = pvpbuf; 973 while (*rvp != NULL && (**rvp & 0377) != endtoken) 974 { 975 int nodetype = **rvp & 0377; 976 977 if (nodetype != CANONHOST && nodetype != CANONUSER) 978 { 979 rvp++; 980 continue; 981 } 982 983 *rvp++ = NULL; 984 985 if (xpvp != NULL) 986 { 987 cataddr(xpvp, NULL, replac, 988 &pvpbuf[sizeof pvpbuf] - replac, 989 '\0'); 990 *++arg_rvp = replac; 991 replac += strlen(replac) + 1; 992 xpvp = NULL; 993 } 994 switch (nodetype) 995 { 996 case CANONHOST: 997 xpvp = rvp; 998 break; 999 1000 case CANONUSER: 1001 default_rvp = rvp; 1002 break; 1003 } 1004 } 1005 if (*rvp != NULL) 1006 *rvp++ = NULL; 1007 if (xpvp != NULL) 1008 { 1009 cataddr(xpvp, NULL, replac, 1010 &pvpbuf[sizeof pvpbuf] - replac, 1011 '\0'); 1012 *++arg_rvp = replac; 1013 } 1014 *++arg_rvp = NULL; 1015 1016 /* save the remainder of the input string */ 1017 trsize = (int) (avp - rvp + 1) * sizeof *rvp; 1018 bcopy((char *) rvp, (char *) pvpb1, trsize); 1019 1020 /* look it up */ 1021 cataddr(key_rvp, NULL, buf, sizeof buf, '\0'); 1022 argvect[0] = buf; 1023 if (map != NULL && bitset(MF_OPEN, map->s_map.map_mflags)) 1024 { 1025 auto int stat = EX_OK; 1026 1027 /* XXX should try to auto-open the map here */ 1028 1029 if (tTd(60, 1)) 1030 printf("map_lookup(%s, %s) => ", 1031 mapname, buf); 1032 replac = (*map->s_map.map_class->map_lookup)(&map->s_map, 1033 buf, argvect, &stat); 1034 if (tTd(60, 1)) 1035 printf("%s (%d)\n", 1036 replac ? replac : "NOT FOUND", 1037 stat); 1038 1039 /* should recover if stat == EX_TEMPFAIL */ 1040 if (stat == EX_TEMPFAIL) 1041 rstat = stat; 1042 } 1043 else 1044 replac = NULL; 1045 1046 /* if no replacement, use default */ 1047 if (replac == NULL && default_rvp != NULL) 1048 { 1049 /* create the default */ 1050 cataddr(default_rvp, NULL, buf, sizeof buf, '\0'); 1051 replac = buf; 1052 } 1053 1054 if (replac == NULL) 1055 { 1056 xpvp = key_rvp; 1057 } 1058 else 1059 { 1060 /* scan the new replacement */ 1061 xpvp = prescan(replac, '\0', pvpbuf, NULL); 1062 if (xpvp == NULL) 1063 { 1064 /* prescan already printed error */ 1065 return EX_DATAERR; 1066 } 1067 } 1068 1069 /* append it to the token list */ 1070 for (avp = hbrvp; *xpvp != NULL; xpvp++) 1071 { 1072 *avp++ = newstr(*xpvp); 1073 if (avp >= &npvp[MAXATOM]) 1074 goto toolong; 1075 } 1076 1077 /* restore the old trailing information */ 1078 for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) 1079 if (avp >= &npvp[MAXATOM]) 1080 goto toolong; 1081 1082 break; 1083 } 1084 1085 /* 1086 ** Check for subroutine calls. 1087 */ 1088 1089 if (*npvp != NULL && (**npvp & 0377) == CALLSUBR) 1090 { 1091 int stat; 1092 1093 bcopy((char *) &npvp[2], (char *) pvp, 1094 (int) (avp - npvp - 2) * sizeof *avp); 1095 if (tTd(21, 3)) 1096 printf("-----callsubr %s\n", npvp[1]); 1097 stat = rewrite(pvp, atoi(npvp[1]), e); 1098 if (rstat == EX_OK || stat == EX_TEMPFAIL) 1099 rstat = stat; 1100 if ((**pvp & 0377) == CANONNET) 1101 rwr = NULL; 1102 } 1103 else 1104 { 1105 bcopy((char *) npvp, (char *) pvp, 1106 (int) (avp - npvp) * sizeof *avp); 1107 } 1108 if (tTd(21, 4)) 1109 { 1110 printf("rewritten as:"); 1111 printav(pvp); 1112 } 1113 } 1114 1115 if (OpMode == MD_TEST || tTd(21, 2)) 1116 { 1117 printf("rewrite: ruleset %2d returns:", ruleset); 1118 printav(pvp); 1119 } 1120 1121 return rstat; 1122 } 1123 /* 1124 ** BUILDADDR -- build address from token vector. 1125 ** 1126 ** Parameters: 1127 ** tv -- token vector. 1128 ** a -- pointer to address descriptor to fill. 1129 ** If NULL, one will be allocated. 1130 ** flags -- info regarding whether this is a sender or 1131 ** a recipient. 1132 ** e -- the current envelope. 1133 ** 1134 ** Returns: 1135 ** NULL if there was an error. 1136 ** 'a' otherwise. 1137 ** 1138 ** Side Effects: 1139 ** fills in 'a' 1140 */ 1141 1142 struct errcodes 1143 { 1144 char *ec_name; /* name of error code */ 1145 int ec_code; /* numeric code */ 1146 } ErrorCodes[] = 1147 { 1148 "usage", EX_USAGE, 1149 "nouser", EX_NOUSER, 1150 "nohost", EX_NOHOST, 1151 "unavailable", EX_UNAVAILABLE, 1152 "software", EX_SOFTWARE, 1153 "tempfail", EX_TEMPFAIL, 1154 "protocol", EX_PROTOCOL, 1155 #ifdef EX_CONFIG 1156 "config", EX_CONFIG, 1157 #endif 1158 NULL, EX_UNAVAILABLE, 1159 }; 1160 1161 ADDRESS * 1162 buildaddr(tv, a, flags, e) 1163 register char **tv; 1164 register ADDRESS *a; 1165 int flags; 1166 register ENVELOPE *e; 1167 { 1168 struct mailer **mp; 1169 register struct mailer *m; 1170 char *bp; 1171 int spaceleft; 1172 static MAILER errormailer; 1173 static char *errorargv[] = { "ERROR", NULL }; 1174 static char buf[MAXNAME]; 1175 1176 if (a == NULL) 1177 a = (ADDRESS *) xalloc(sizeof *a); 1178 bzero((char *) a, sizeof *a); 1179 1180 /* figure out what net/mailer to use */ 1181 if (*tv == NULL || (**tv & 0377) != CANONNET) 1182 { 1183 syserr("554 buildaddr: no net"); 1184 badaddr: 1185 a->q_flags |= QBADADDR; 1186 a->q_mailer = &errormailer; 1187 if (errormailer.m_name == NULL) 1188 { 1189 /* initialize the bogus mailer */ 1190 errormailer.m_name = "*error*"; 1191 errormailer.m_mailer = "ERROR"; 1192 errormailer.m_argv = errorargv; 1193 } 1194 return a; 1195 } 1196 tv++; 1197 if (strcasecmp(*tv, "error") == 0) 1198 { 1199 if ((**++tv & 0377) == CANONHOST) 1200 { 1201 register struct errcodes *ep; 1202 1203 if (isascii(**++tv) && isdigit(**tv)) 1204 { 1205 setstat(atoi(*tv)); 1206 } 1207 else 1208 { 1209 for (ep = ErrorCodes; ep->ec_name != NULL; ep++) 1210 if (strcasecmp(ep->ec_name, *tv) == 0) 1211 break; 1212 setstat(ep->ec_code); 1213 } 1214 tv++; 1215 } 1216 if ((**tv & 0377) != CANONUSER) 1217 syserr("554 buildaddr: error: no user"); 1218 cataddr(++tv, NULL, buf, sizeof buf, ' '); 1219 stripquotes(buf); 1220 usrerr(buf); 1221 goto badaddr; 1222 } 1223 1224 for (mp = Mailer; (m = *mp++) != NULL; ) 1225 { 1226 if (strcasecmp(m->m_name, *tv) == 0) 1227 break; 1228 } 1229 if (m == NULL) 1230 { 1231 syserr("554 buildaddr: unknown mailer %s", *tv); 1232 goto badaddr; 1233 } 1234 a->q_mailer = m; 1235 1236 /* figure out what host (if any) */ 1237 tv++; 1238 if ((**tv & 0377) == CANONHOST) 1239 { 1240 bp = buf; 1241 spaceleft = sizeof buf - 1; 1242 while (*++tv != NULL && (**tv & 0377) != CANONUSER) 1243 { 1244 int i = strlen(*tv); 1245 1246 if (i > spaceleft) 1247 { 1248 /* out of space for this address */ 1249 if (spaceleft >= 0) 1250 syserr("554 buildaddr: host too long (%.40s...)", 1251 buf); 1252 i = spaceleft; 1253 spaceleft = 0; 1254 } 1255 if (i <= 0) 1256 continue; 1257 bcopy(*tv, bp, i); 1258 bp += i; 1259 spaceleft -= i; 1260 } 1261 *bp = '\0'; 1262 a->q_host = newstr(buf); 1263 } 1264 else 1265 { 1266 if (!bitnset(M_LOCALMAILER, m->m_flags)) 1267 { 1268 syserr("554 buildaddr: no host"); 1269 goto badaddr; 1270 } 1271 a->q_host = NULL; 1272 } 1273 1274 /* figure out the user */ 1275 if (*tv == NULL || (**tv & 0377) != CANONUSER) 1276 { 1277 syserr("554 buildaddr: no user"); 1278 goto badaddr; 1279 } 1280 tv++; 1281 1282 /* do special mapping for local mailer */ 1283 if (m == LocalMailer && *tv != NULL) 1284 { 1285 register char *p = *tv; 1286 1287 if (*p == '"') 1288 p++; 1289 if (*p == '|') 1290 a->q_mailer = m = ProgMailer; 1291 else if (*p == '/') 1292 a->q_mailer = m = FileMailer; 1293 else if (*p == ':') 1294 { 1295 /* may be :include: */ 1296 cataddr(tv, NULL, buf, sizeof buf, '\0'); 1297 stripquotes(buf); 1298 if (strncasecmp(buf, ":include:", 9) == 0) 1299 { 1300 /* if :include:, don't need further rewriting */ 1301 a->q_mailer = m = InclMailer; 1302 a->q_user = &buf[9]; 1303 return (a); 1304 } 1305 } 1306 } 1307 1308 if (m == LocalMailer && *tv != NULL && strcmp(*tv, "@") == 0) 1309 { 1310 tv++; 1311 a->q_flags |= QNOTREMOTE; 1312 } 1313 1314 /* rewrite according recipient mailer rewriting rules */ 1315 define('h', a->q_host, e); 1316 if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags)) 1317 { 1318 /* sender addresses done later */ 1319 (void) rewrite(tv, 2, e); 1320 if (m->m_re_rwset > 0) 1321 (void) rewrite(tv, m->m_re_rwset, e); 1322 } 1323 (void) rewrite(tv, 4, e); 1324 1325 /* save the result for the command line/RCPT argument */ 1326 cataddr(tv, NULL, buf, sizeof buf, '\0'); 1327 a->q_user = buf; 1328 1329 /* 1330 ** Do mapping to lower case as requested by mailer 1331 */ 1332 1333 if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags)) 1334 makelower(a->q_host); 1335 if (!bitnset(M_USR_UPPER, m->m_flags)) 1336 makelower(a->q_user); 1337 1338 return (a); 1339 } 1340 /* 1341 ** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs) 1342 ** 1343 ** Parameters: 1344 ** pvp -- parameter vector to rebuild. 1345 ** evp -- last parameter to include. Can be NULL to 1346 ** use entire pvp. 1347 ** buf -- buffer to build the string into. 1348 ** sz -- size of buf. 1349 ** spacesub -- the space separator character; if null, 1350 ** use SpaceSub. 1351 ** 1352 ** Returns: 1353 ** none. 1354 ** 1355 ** Side Effects: 1356 ** Destroys buf. 1357 */ 1358 1359 cataddr(pvp, evp, buf, sz, spacesub) 1360 char **pvp; 1361 char **evp; 1362 char *buf; 1363 register int sz; 1364 char spacesub; 1365 { 1366 bool oatomtok = FALSE; 1367 bool natomtok = FALSE; 1368 register int i; 1369 register char *p; 1370 1371 if (spacesub == '\0') 1372 spacesub = SpaceSub; 1373 1374 if (pvp == NULL) 1375 { 1376 (void) strcpy(buf, ""); 1377 return; 1378 } 1379 p = buf; 1380 sz -= 2; 1381 while (*pvp != NULL && (i = strlen(*pvp)) < sz) 1382 { 1383 natomtok = (toktype(**pvp) == ATM); 1384 if (oatomtok && natomtok) 1385 *p++ = spacesub; 1386 (void) strcpy(p, *pvp); 1387 oatomtok = natomtok; 1388 p += i; 1389 sz -= i + 1; 1390 if (pvp++ == evp) 1391 break; 1392 } 1393 *p = '\0'; 1394 } 1395 /* 1396 ** SAMEADDR -- Determine if two addresses are the same 1397 ** 1398 ** This is not just a straight comparison -- if the mailer doesn't 1399 ** care about the host we just ignore it, etc. 1400 ** 1401 ** Parameters: 1402 ** a, b -- pointers to the internal forms to compare. 1403 ** 1404 ** Returns: 1405 ** TRUE -- they represent the same mailbox. 1406 ** FALSE -- they don't. 1407 ** 1408 ** Side Effects: 1409 ** none. 1410 */ 1411 1412 bool 1413 sameaddr(a, b) 1414 register ADDRESS *a; 1415 register ADDRESS *b; 1416 { 1417 /* if they don't have the same mailer, forget it */ 1418 if (a->q_mailer != b->q_mailer) 1419 return (FALSE); 1420 1421 /* if the user isn't the same, we can drop out */ 1422 if (strcmp(a->q_user, b->q_user) != 0) 1423 return (FALSE); 1424 1425 /* if we have good uids for both but the differ, these are different */ 1426 if (bitset(QGOODUID, a->q_flags & b->q_flags) && a->q_uid != b->q_uid) 1427 return (FALSE); 1428 1429 /* otherwise compare hosts (but be careful for NULL ptrs) */ 1430 if (a->q_host == b->q_host) 1431 { 1432 /* probably both null pointers */ 1433 return (TRUE); 1434 } 1435 if (a->q_host == NULL || b->q_host == NULL) 1436 { 1437 /* only one is a null pointer */ 1438 return (FALSE); 1439 } 1440 if (strcmp(a->q_host, b->q_host) != 0) 1441 return (FALSE); 1442 1443 return (TRUE); 1444 } 1445 /* 1446 ** PRINTADDR -- print address (for debugging) 1447 ** 1448 ** Parameters: 1449 ** a -- the address to print 1450 ** follow -- follow the q_next chain. 1451 ** 1452 ** Returns: 1453 ** none. 1454 ** 1455 ** Side Effects: 1456 ** none. 1457 */ 1458 1459 printaddr(a, follow) 1460 register ADDRESS *a; 1461 bool follow; 1462 { 1463 bool first = TRUE; 1464 register MAILER *m; 1465 MAILER pseudomailer; 1466 1467 while (a != NULL) 1468 { 1469 first = FALSE; 1470 printf("%x=", a); 1471 (void) fflush(stdout); 1472 1473 /* find the mailer -- carefully */ 1474 m = a->q_mailer; 1475 if (m == NULL) 1476 { 1477 m = &pseudomailer; 1478 m->m_mno = -1; 1479 m->m_name = "NULL"; 1480 } 1481 1482 printf("%s:\n\tmailer %d (%s), host `%s', user `%s', ruser `%s'\n", 1483 a->q_paddr, m->m_mno, m->m_name, 1484 a->q_host, a->q_user, 1485 a->q_ruser ? a->q_ruser : "<null>"); 1486 printf("\tnext=%x, flags=%o, alias %x, uid %d, gid %d\n", 1487 a->q_next, a->q_flags, a->q_alias, a->q_uid, a->q_gid); 1488 printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n", 1489 a->q_owner == NULL ? "(none)" : a->q_owner, 1490 a->q_home == NULL ? "(none)" : a->q_home, 1491 a->q_fullname == NULL ? "(none)" : a->q_fullname); 1492 1493 if (!follow) 1494 return; 1495 a = a->q_next; 1496 } 1497 if (first) 1498 printf("[NULL]\n"); 1499 } 1500 1501 /* 1502 ** REMOTENAME -- return the name relative to the current mailer 1503 ** 1504 ** Parameters: 1505 ** name -- the name to translate. 1506 ** m -- the mailer that we want to do rewriting relative 1507 ** to. 1508 ** flags -- fine tune operations. 1509 ** pstat -- pointer to status word. 1510 ** e -- the current envelope. 1511 ** 1512 ** Returns: 1513 ** the text string representing this address relative to 1514 ** the receiving mailer. 1515 ** 1516 ** Side Effects: 1517 ** none. 1518 ** 1519 ** Warnings: 1520 ** The text string returned is tucked away locally; 1521 ** copy it if you intend to save it. 1522 */ 1523 1524 char * 1525 remotename(name, m, flags, pstat, e) 1526 char *name; 1527 struct mailer *m; 1528 int flags; 1529 int *pstat; 1530 register ENVELOPE *e; 1531 { 1532 register char **pvp; 1533 char *fancy; 1534 char *oldg = macvalue('g', e); 1535 int rwset; 1536 static char buf[MAXNAME]; 1537 char lbuf[MAXNAME]; 1538 char pvpbuf[PSBUFSIZE]; 1539 extern char *crackaddr(); 1540 1541 if (tTd(12, 1)) 1542 printf("remotename(%s)\n", name); 1543 1544 /* don't do anything if we are tagging it as special */ 1545 if (bitset(RF_SENDERADDR, flags)) 1546 rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset 1547 : m->m_se_rwset; 1548 else 1549 rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset 1550 : m->m_re_rwset; 1551 if (rwset < 0) 1552 return (name); 1553 1554 /* 1555 ** Do a heuristic crack of this name to extract any comment info. 1556 ** This will leave the name as a comment and a $g macro. 1557 */ 1558 1559 if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags)) 1560 fancy = "\201g"; 1561 else 1562 fancy = crackaddr(name); 1563 1564 /* 1565 ** Turn the name into canonical form. 1566 ** Normally this will be RFC 822 style, i.e., "user@domain". 1567 ** If this only resolves to "user", and the "C" flag is 1568 ** specified in the sending mailer, then the sender's 1569 ** domain will be appended. 1570 */ 1571 1572 pvp = prescan(name, '\0', pvpbuf, NULL); 1573 if (pvp == NULL) 1574 return (name); 1575 if (rewrite(pvp, 3, e) == EX_TEMPFAIL) 1576 *pstat = EX_TEMPFAIL; 1577 if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL) 1578 { 1579 /* append from domain to this address */ 1580 register char **pxp = pvp; 1581 1582 /* see if there is an "@domain" in the current name */ 1583 while (*pxp != NULL && strcmp(*pxp, "@") != 0) 1584 pxp++; 1585 if (*pxp == NULL) 1586 { 1587 /* no.... append the "@domain" from the sender */ 1588 register char **qxq = e->e_fromdomain; 1589 1590 while ((*pxp++ = *qxq++) != NULL) 1591 continue; 1592 if (rewrite(pvp, 3, e) == EX_TEMPFAIL) 1593 *pstat = EX_TEMPFAIL; 1594 } 1595 } 1596 1597 /* 1598 ** Do more specific rewriting. 1599 ** Rewrite using ruleset 1 or 2 depending on whether this is 1600 ** a sender address or not. 1601 ** Then run it through any receiving-mailer-specific rulesets. 1602 */ 1603 1604 if (bitset(RF_SENDERADDR, flags)) 1605 { 1606 if (rewrite(pvp, 1, e) == EX_TEMPFAIL) 1607 *pstat = EX_TEMPFAIL; 1608 } 1609 else 1610 { 1611 if (rewrite(pvp, 2, e) == EX_TEMPFAIL) 1612 *pstat = EX_TEMPFAIL; 1613 } 1614 if (rwset > 0) 1615 { 1616 if (rewrite(pvp, rwset, e) == EX_TEMPFAIL) 1617 *pstat = EX_TEMPFAIL; 1618 } 1619 1620 /* 1621 ** Do any final sanitation the address may require. 1622 ** This will normally be used to turn internal forms 1623 ** (e.g., user@host.LOCAL) into external form. This 1624 ** may be used as a default to the above rules. 1625 */ 1626 1627 if (rewrite(pvp, 4, e) == EX_TEMPFAIL) 1628 *pstat = EX_TEMPFAIL; 1629 1630 /* 1631 ** Now restore the comment information we had at the beginning. 1632 */ 1633 1634 cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0'); 1635 define('g', lbuf, e); 1636 expand(fancy, buf, &buf[sizeof buf - 1], e); 1637 define('g', oldg, e); 1638 1639 if (tTd(12, 1)) 1640 printf("remotename => `%s'\n", buf); 1641 return (buf); 1642 } 1643 /* 1644 ** MAPLOCALUSER -- run local username through ruleset 5 for final redirection 1645 ** 1646 ** Parameters: 1647 ** a -- the address to map (but just the user name part). 1648 ** sendq -- the sendq in which to install any replacement 1649 ** addresses. 1650 ** 1651 ** Returns: 1652 ** none. 1653 */ 1654 1655 maplocaluser(a, sendq, e) 1656 register ADDRESS *a; 1657 ADDRESS **sendq; 1658 ENVELOPE *e; 1659 { 1660 register char **pvp; 1661 register ADDRESS *a1 = NULL; 1662 auto char *delimptr; 1663 char pvpbuf[PSBUFSIZE]; 1664 1665 if (tTd(29, 1)) 1666 { 1667 printf("maplocaluser: "); 1668 printaddr(a, FALSE); 1669 } 1670 pvp = prescan(a->q_user, '\0', pvpbuf, &delimptr); 1671 if (pvp == NULL) 1672 return; 1673 1674 (void) rewrite(pvp, 5, e); 1675 if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) 1676 return; 1677 1678 /* if non-null, mailer destination specified -- has it changed? */ 1679 a1 = buildaddr(pvp, NULL, 0, e); 1680 if (a1 == NULL || sameaddr(a, a1)) 1681 return; 1682 1683 /* mark old address as dead; insert new address */ 1684 a->q_flags |= QDONTSEND; 1685 if (tTd(29, 5)) 1686 { 1687 printf("maplocaluser: QDONTSEND "); 1688 printaddr(a, FALSE); 1689 } 1690 a1->q_alias = a; 1691 allocaddr(a1, RF_COPYALL, NULL, delimptr); 1692 (void) recipient(a1, sendq, e); 1693 } 1694 /* 1695 ** DEQUOTE_INIT -- initialize dequote map 1696 ** 1697 ** This is a no-op. 1698 ** 1699 ** Parameters: 1700 ** map -- the internal map structure. 1701 ** args -- arguments. 1702 ** 1703 ** Returns: 1704 ** TRUE. 1705 */ 1706 1707 bool 1708 dequote_init(map, args) 1709 MAP *map; 1710 char *args; 1711 { 1712 register char *p = args; 1713 1714 for (;;) 1715 { 1716 while (isascii(*p) && isspace(*p)) 1717 p++; 1718 if (*p != '-') 1719 break; 1720 switch (*++p) 1721 { 1722 case 'a': 1723 map->map_app = ++p; 1724 break; 1725 } 1726 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1727 p++; 1728 if (*p != '\0') 1729 *p = '\0'; 1730 } 1731 if (map->map_app != NULL) 1732 map->map_app = newstr(map->map_app); 1733 1734 return TRUE; 1735 } 1736 /* 1737 ** DEQUOTE_MAP -- unquote an address 1738 ** 1739 ** Parameters: 1740 ** map -- the internal map structure (ignored). 1741 ** name -- the name to dequote. 1742 ** av -- arguments (ignored). 1743 ** statp -- pointer to status out-parameter. 1744 ** 1745 ** Returns: 1746 ** NULL -- if there were no quotes, or if the resulting 1747 ** unquoted buffer would not be acceptable to prescan. 1748 ** else -- The dequoted buffer. 1749 */ 1750 1751 char * 1752 dequote_map(map, name, av, statp) 1753 MAP *map; 1754 char *name; 1755 char **av; 1756 int *statp; 1757 { 1758 register char *p; 1759 register char *q; 1760 register char c; 1761 int anglecnt; 1762 int cmntcnt; 1763 int quotecnt; 1764 int spacecnt; 1765 bool quotemode; 1766 bool bslashmode; 1767 1768 anglecnt = 0; 1769 cmntcnt = 0; 1770 quotecnt = 0; 1771 spacecnt = 0; 1772 quotemode = FALSE; 1773 bslashmode = FALSE; 1774 1775 for (p = q = name; (c = *p++) != '\0'; ) 1776 { 1777 if (bslashmode) 1778 { 1779 bslashmode = FALSE; 1780 *q++ = c; 1781 continue; 1782 } 1783 1784 switch (c) 1785 { 1786 case '\\': 1787 bslashmode = TRUE; 1788 break; 1789 1790 case '(': 1791 cmntcnt++; 1792 break; 1793 1794 case ')': 1795 if (cmntcnt-- <= 0) 1796 return NULL; 1797 break; 1798 1799 case ' ': 1800 spacecnt++; 1801 break; 1802 } 1803 1804 if (cmntcnt > 0) 1805 { 1806 *q++ = c; 1807 continue; 1808 } 1809 1810 switch (c) 1811 { 1812 case '"': 1813 quotemode = !quotemode; 1814 quotecnt++; 1815 continue; 1816 1817 case '<': 1818 anglecnt++; 1819 break; 1820 1821 case '>': 1822 if (anglecnt-- <= 0) 1823 return NULL; 1824 break; 1825 } 1826 *q++ = c; 1827 } 1828 1829 if (anglecnt != 0 || cmntcnt != 0 || bslashmode || 1830 quotemode || quotecnt <= 0 || spacecnt != 0) 1831 return NULL; 1832 *q++ = '\0'; 1833 return name; 1834 } 1835