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