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