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