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