fix improper interpretation of % in addresses in $#error mailer
[unix-history] / usr / src / usr.sbin / sendmail / src / parseaddr.c
CommitLineData
f0e070dc 1/*
dc45ba8c 2 * Copyright (c) 1983 Eric P. Allman
c1f0acb8
KB
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
bee79b64 5 *
417f7a11 6 * %sccs.include.redist.c%
bee79b64 7 */
f0e070dc
MAN
8
9#ifndef lint
f51fdacb 10static char sccsid[] = "@(#)parseaddr.c 8.10 (Berkeley) %G%";
bee79b64 11#endif /* not lint */
f0e070dc 12
fd6cfc53
EA
13#include "sendmail.h"
14
15#ifdef CC_WONT_PROMOTE
16static int toktype __P((char));
17#else /* !CC_WONT_PROMOTE */
18static int toktype __P((int)); /* char -> int */
19#endif /* CC_WONT_PROMOTE */
20static void _rewrite __P((char **, int));
21static void callsubr __P((char **));
22static ADDRESS * buildaddr __P((char **, ADDRESS *));
23static void uurelativize __P((const char *, const char *, char **));
24
25char *DelimChar; /* set to point to the delimiter */
916b3375 26
b3cbe40f 27/*
40d27fed 28** PARSEADDR -- Parse an address
b3cbe40f
EA
29**
30** Parses an address and breaks it up into three parts: a
31** net to transmit the message on, the host to transmit it
32** to, and a user on that host. These are loaded into an
406f98bc 33** ADDRESS header with the values squirreled away if necessary.
b3cbe40f
EA
34** The "user" part may not be a real user; the process may
35** just reoccur on that machine. For example, on a machine
36** with an arpanet connection, the address
37** csvax.bill@berkeley
38** will break up to a "user" of 'csvax.bill' and a host
39** of 'berkeley' -- to be transmitted over the arpanet.
40**
41** Parameters:
42** addr -- the address to parse.
43** a -- a pointer to the address descriptor buffer.
44** If NULL, a header will be created.
28f94061
EA
45** flags -- describe detail for parsing. See RF_ definitions
46** in sendmail.h.
d3f52e20
EA
47** delim -- the character to terminate the address, passed
48** to prescan.
9e2cf26f
EA
49** delimptr -- if non-NULL, set to the location of the
50** delim character that was found.
a4076aed 51** e -- the envelope that will contain this address.
b3cbe40f
EA
52**
53** Returns:
54** A pointer to the address descriptor header (`a' if
55** `a' is non-NULL).
56** NULL on error.
57**
58** Side Effects:
59** none
b3cbe40f
EA
60*/
61
7338e3d4 62/* following delimiters are inherent to the internal algorithms */
3f99edce 63# define DELIMCHARS "()<>,;\r\n" /* default word delimiters */
ecf90b7d 64
406f98bc 65ADDRESS *
28f94061 66parseaddr(addr, a, flags, delim, delimptr, e)
b3cbe40f 67 char *addr;
406f98bc 68 register ADDRESS *a;
28f94061 69 int flags;
ef3bc6b1 70 int delim;
9e2cf26f 71 char **delimptr;
a4076aed 72 register ENVELOPE *e;
b3cbe40f 73{
d6a28dd8 74 register char **pvp;
9e2cf26f 75 auto char *delimptrbuf;
d1db7a89 76 bool queueup;
217a0102 77 char pvpbuf[PSBUFSIZE];
b3cbe40f
EA
78
79 /*
80 ** Initialize and prescan address.
81 */
82
a4076aed 83 e->e_to = addr;
9678c96d 84 if (tTd(20, 1))
40d27fed 85 printf("\n--parseaddr(%s)\n", addr);
9e3c0a28 86
ab542b8f
EA
87 if (invalidaddr(addr))
88 {
89 if (tTd(20, 1))
90 printf("parseaddr-->bad address\n");
91 return NULL;
92 }
93
fd6cfc53
EA
94 {
95 extern char *DelimChar; /* parseaddr.c */
96 char savec;
97 bool invalid;
c40bef63
EA
98 extern char *finddelim();
99 extern bool invalidaddr();
fd6cfc53
EA
100
101 DelimChar = finddelim(addr, delim);
102 savec = *DelimChar;
103 *DelimChar = '\0';
104 invalid = invalidaddr(addr);
105 *DelimChar = savec;
106 if (invalid)
107 return (NULL);
108 }
109
9e2cf26f
EA
110 if (delimptr == NULL)
111 delimptr = &delimptrbuf;
112
113 pvp = prescan(addr, delim, pvpbuf, delimptr);
d6a28dd8 114 if (pvp == NULL)
a17692a9
EA
115 {
116 if (tTd(20, 1))
117 printf("parseaddr-->NULL\n");
b3cbe40f 118 return (NULL);
a17692a9 119 }
b3cbe40f 120
dab5fedb
EA
121 /*
122 ** Save addr if we are going to have to.
123 **
124 ** We have to do this early because there is a chance that
125 ** the map lookups in the rewriting rules could clobber
126 ** static memory somewhere.
127 */
128
129 if (bitset(RF_COPYPADDR, flags) && addr != NULL)
130 {
131 char savec = **delimptr;
132
133 if (savec != '\0')
134 **delimptr = '\0';
135 addr = newstr(addr);
136 if (savec != '\0')
137 **delimptr = savec;
138 }
139
b3cbe40f 140 /*
d6a28dd8 141 ** Apply rewriting rules.
0908f182 142 ** Ruleset 0 does basic parsing. It must resolve.
b3cbe40f
EA
143 */
144
d1db7a89
EA
145 queueup = FALSE;
146 if (rewrite(pvp, 3, e) == EX_TEMPFAIL)
147 queueup = TRUE;
148 if (rewrite(pvp, 0, e) == EX_TEMPFAIL)
149 queueup = TRUE;
b3cbe40f 150
b3cbe40f
EA
151
152 /*
d6a28dd8 153 ** Build canonical address from pvp.
b3cbe40f
EA
154 */
155
28f94061 156 a = buildaddr(pvp, a, flags, e);
b3cbe40f
EA
157
158 /*
d6a28dd8
EA
159 ** Make local copies of the host & user and then
160 ** transport them out.
b3cbe40f
EA
161 */
162
dab5fedb 163 allocaddr(a, flags, addr);
9577b1df
EA
164 if (bitset(QBADADDR, a->q_flags))
165 return a;
d13779b1 166
d1db7a89
EA
167 /*
168 ** If there was a parsing failure, mark it for queueing.
169 */
170
171 if (queueup)
c281eee3 172 {
7ffb494c
EA
173 char *msg = "Transient parse error -- message queued for future delivery";
174
c281eee3
EA
175 if (tTd(20, 1))
176 printf("parseaddr: queuing message\n");
7ffb494c
EA
177 message(msg);
178 if (e->e_message == NULL)
f10d3e38 179 e->e_message = newstr(msg);
d1db7a89 180 a->q_flags |= QQUEUEUP;
c281eee3 181 }
d1db7a89 182
d13779b1
EA
183 /*
184 ** Compute return value.
185 */
186
187 if (tTd(20, 1))
188 {
189 printf("parseaddr-->");
190 printaddr(a, FALSE);
191 }
192
193 return (a);
194}
195\f/*
ab542b8f
EA
196** INVALIDADDR -- check for address containing meta-characters
197**
198** Parameters:
199** addr -- the address to check.
200**
201** Returns:
202** TRUE -- if the address has any "wierd" characters
203** FALSE -- otherwise.
204*/
205
206bool
207invalidaddr(addr)
208 register char *addr;
209{
210 for (; *addr != '\0'; addr++)
211 {
2bee003d 212 if ((*addr & 0340) != 0200)
ab542b8f
EA
213 continue;
214 setstat(EX_USAGE);
b6edea3d 215 usrerr("553 Address contained invalid control characters");
ab542b8f
EA
216 return TRUE;
217 }
218 return FALSE;
219}
220\f/*
d13779b1
EA
221** ALLOCADDR -- do local allocations of address on demand.
222**
223** Also lowercases the host name if requested.
224**
225** Parameters:
226** a -- the address to reallocate.
28f94061
EA
227** flags -- the copy flag (see RF_ definitions in sendmail.h
228** for a description).
d13779b1
EA
229** paddr -- the printname of the address.
230**
231** Returns:
232** none.
233**
234** Side Effects:
235** Copies portions of a into local buffers as requested.
236*/
237
dab5fedb 238allocaddr(a, flags, paddr)
d13779b1 239 register ADDRESS *a;
28f94061 240 int flags;
d13779b1
EA
241 char *paddr;
242{
7002d4e7 243 if (tTd(24, 4))
28f94061 244 printf("allocaddr(flags=%o, paddr=%s)\n", flags, paddr);
7002d4e7 245
dab5fedb 246 a->q_paddr = paddr;
2e3062fe
EA
247
248 if (a->q_user == NULL)
249 a->q_user = "";
250 if (a->q_host == NULL)
251 a->q_host = "";
252
28f94061 253 if (bitset(RF_COPYPARSE, flags))
b3cbe40f 254 {
2e3062fe 255 a->q_host = newstr(a->q_host);
d6a28dd8
EA
256 if (a->q_user != a->q_paddr)
257 a->q_user = newstr(a->q_user);
b3cbe40f
EA
258 }
259
d13779b1
EA
260 if (a->q_paddr == NULL)
261 a->q_paddr = a->q_user;
0fe3917f
EA
262}
263\f/*
fd6cfc53
EA
264** INVALIDADDR -- check an address string for invalid control characters.
265**
266** Parameters:
267** addr -- address string to be checked.
268**
269** Returns:
270** TRUE if address string could cause problems, FALSE o/w.
271**
272** Side Effects:
273** ExitStat may be changed and an error message generated.
274*/
275
276bool
277invalidaddr(addr)
278 const char *addr;
279{
280 register const char *cp;
281
282 /* make sure error messages don't have garbage on them */
283 errno = 0;
284
285 /*
286 ** Sendmail reserves characters 020 - 036 for rewriting rules
287 ** which can cause havoc (e.g. infinite rewriting loops) if
288 ** one shows up at the wrong time. If any of these characters
289 ** appear in an address, the address is deemed "invalid" and
290 ** an error message is generated.
291 */
292
293 for (cp = addr; *cp; cp++)
294 if ((*cp >= MATCHZANY && *cp <= HOSTEND) || *cp == '\001')
295 {
296 setstat(EX_USAGE);
297 usrerr("address contained invalid control char(s)");
298 return (TRUE);
299 }
300 return (FALSE);
301}
302\f/*
b3cbe40f
EA
303** PRESCAN -- Prescan name and make it canonical
304**
7338e3d4
EA
305** Scans a name and turns it into a set of tokens. This process
306** deletes blanks and comments (in parentheses).
b3cbe40f
EA
307**
308** This routine knows about quoted strings and angle brackets.
309**
310** There are certain subtleties to this routine. The one that
311** comes to mind now is that backslashes on the ends of names
312** are silently stripped off; this is intentional. The problem
313** is that some versions of sndmsg (like at LBL) set the kill
314** character to something other than @ when reading addresses;
315** so people type "csvax.eric\@berkeley" -- which screws up the
316** berknet mailer.
317**
318** Parameters:
319** addr -- the name to chomp.
b3cbe40f
EA
320** delim -- the delimiter for the address, normally
321** '\0' or ','; \0 is accepted in any case.
f12f79be 322** If '\t' then we are reading the .cf file.
217a0102
EA
323** pvpbuf -- place to put the saved text -- note that
324** the pointers are static.
9e2cf26f
EA
325** delimptr -- if non-NULL, set to the location of the
326** terminating delimiter.
b3cbe40f
EA
327**
328** Returns:
d6a28dd8 329** A pointer to a vector of tokens.
b3cbe40f 330** NULL on error.
b3cbe40f
EA
331*/
332
506fc377
EA
333/* states and character types */
334# define OPR 0 /* operator */
335# define ATM 1 /* atom */
336# define QST 2 /* in quoted string */
337# define SPC 3 /* chewing up spaces */
338# define ONE 4 /* pick up one character */
339
340# define NSTATES 5 /* number of states */
341# define TYPE 017 /* mask to select state type */
342
343/* meta bits for table */
344# define M 020 /* meta character; don't pass through */
345# define B 040 /* cause a break */
346# define MB M|B /* meta-break */
347
348static short StateTab[NSTATES][NSTATES] =
349{
921e1125 350 /* oldst chtype> OPR ATM QST SPC ONE */
1f513630
EA
351 /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B,
352 /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B,
353 /*QST*/ QST, QST, OPR, QST, QST,
506fc377
EA
354 /*SPC*/ OPR, ATM, QST, SPC|M, ONE,
355 /*ONE*/ OPR, OPR, OPR, OPR, OPR,
356};
357
358# define NOCHAR -1 /* signal nothing in lookahead token */
359
d6a28dd8 360char **
9e2cf26f 361prescan(addr, delim, pvpbuf, delimptr)
b3cbe40f 362 char *addr;
b3cbe40f 363 char delim;
217a0102 364 char pvpbuf[];
9e2cf26f 365 char **delimptr;
b3cbe40f
EA
366{
367 register char *p;
506fc377 368 register char *q;
611b763d 369 register int c;
d6a28dd8 370 char **avp;
b3cbe40f
EA
371 bool bslashmode;
372 int cmntcnt;
e93460a9 373 int anglecnt;
d6a28dd8 374 char *tok;
506fc377
EA
375 int state;
376 int newstate;
fba945cd 377 char *saveto = CurEnv->e_to;
506fc377 378 static char *av[MAXATOM+1];
15cd119d
EA
379
380 /* make sure error messages don't have garbage on them */
381 errno = 0;
b3cbe40f 382
217a0102 383 q = pvpbuf;
d6a28dd8 384 bslashmode = FALSE;
43ff8bb5 385 cmntcnt = 0;
e93460a9 386 anglecnt = 0;
d6a28dd8 387 avp = av;
85c61679 388 state = ATM;
506fc377
EA
389 c = NOCHAR;
390 p = addr;
fba945cd 391 CurEnv->e_to = p;
97a521ff 392 if (tTd(22, 11))
506fc377
EA
393 {
394 printf("prescan: ");
395 xputs(p);
03388044 396 (void) putchar('\n');
506fc377 397 }
506fc377
EA
398
399 do
b3cbe40f 400 {
d6a28dd8
EA
401 /* read a token */
402 tok = q;
506fc377 403 for (;;)
b3cbe40f 404 {
506fc377 405 /* store away any old lookahead character */
e9937bb7 406 if (c != NOCHAR && !bslashmode)
506fc377 407 {
f12f79be 408 /* see if there is room */
217a0102 409 if (q >= &pvpbuf[PSBUFSIZE - 5])
506fc377 410 {
b6edea3d 411 usrerr("553 Address too long");
9e2cf26f
EA
412 if (delimptr != NULL)
413 *delimptr = p;
fba945cd 414 CurEnv->e_to = saveto;
506fc377
EA
415 return (NULL);
416 }
f12f79be
EA
417
418 /* squirrel it away */
506fc377
EA
419 *q++ = c;
420 }
421
422 /* read a new input character */
423 c = *p++;
422bed79 424 if (c == '\0')
97a521ff
EA
425 {
426 /* diagnose and patch up bad syntax */
427 if (state == QST)
428 {
40d84045 429 usrerr("653 Unbalanced '\"' (fixed)");
97a521ff
EA
430 c = '"';
431 }
432 else if (cmntcnt > 0)
433 {
40d84045 434 usrerr("653 Unbalanced '(' (fixed)");
97a521ff
EA
435 c = ')';
436 }
437 else if (anglecnt > 0)
438 {
439 c = '>';
40d84045 440 usrerr("653 Unbalanced '<' (fixed)");
97a521ff
EA
441 }
442 else
443 break;
444
445 p--;
446 }
422bed79
EA
447 else if (c == delim && anglecnt <= 0 &&
448 cmntcnt <= 0 && state != QST)
449 break;
f12f79be 450
506fc377
EA
451 if (tTd(22, 101))
452 printf("c=%c, s=%d; ", c, state);
506fc377 453
d6a28dd8
EA
454 /* chew up special characters */
455 *q = '\0';
456 if (bslashmode)
457 {
ff54f632
EA
458 bslashmode = FALSE;
459
2e3062fe 460 /* kludge \! for naive users */
5d41b806 461 if (cmntcnt > 0)
ff54f632 462 {
5d41b806 463 c = NOCHAR;
ff54f632
EA
464 continue;
465 }
466 else if (c != '!' || state == QST)
467 {
85c61679 468 *q++ = '\\';
ff54f632
EA
469 continue;
470 }
d6a28dd8 471 }
048264ae
EA
472
473 if (c == '\\')
d6a28dd8
EA
474 {
475 bslashmode = TRUE;
d6a28dd8 476 }
fd6cfc53 477 if (state == QST)
63a35680
EA
478 {
479 /* do nothing, just avoid next clauses */
480 }
506fc377 481 else if (c == '(')
cdb17311 482 {
506fc377
EA
483 cmntcnt++;
484 c = NOCHAR;
cdb17311 485 }
506fc377 486 else if (c == ')')
d6a28dd8 487 {
506fc377 488 if (cmntcnt <= 0)
d6a28dd8 489 {
40d84045 490 usrerr("653 Unbalanced ')' (fixed)");
64b7394c 491 c = NOCHAR;
d6a28dd8 492 }
506fc377
EA
493 else
494 cmntcnt--;
d6a28dd8 495 }
506fc377
EA
496 else if (cmntcnt > 0)
497 c = NOCHAR;
e93460a9
EA
498 else if (c == '<')
499 anglecnt++;
500 else if (c == '>')
501 {
502 if (anglecnt <= 0)
503 {
40d84045 504 usrerr("653 Unbalanced '>' (fixed)");
64b7394c 505 c = NOCHAR;
e93460a9 506 }
64b7394c
EA
507 else
508 anglecnt--;
e93460a9 509 }
2bee003d 510 else if (delim == ' ' && isascii(c) && isspace(c))
926671ee 511 c = ' ';
7b955214
EA
512 else if (c == ':' && !CurEnv->e_oldstyle)
513 {
514 /* consume characters until a semicolon */
515 while (*p != '\0' && *p != ';')
516 p++;
517 if (*p == '\0')
518 usrerr("Unbalanced ':...;' group spec");
519 else
520 p++;
521 c = ' ';
522 }
d6a28dd8 523
fd6cfc53
EA
524 else if (c == ';') /* semicolons are not tokens */
525 c = NOCHAR;
526
506fc377
EA
527 if (c == NOCHAR)
528 continue;
d6a28dd8 529
506fc377 530 /* see if this is end of input */
f115fb3a 531 if (c == delim && anglecnt <= 0 && state != QST)
506fc377 532 break;
d6a28dd8 533
506fc377 534 newstate = StateTab[state][toktype(c)];
506fc377
EA
535 if (tTd(22, 101))
536 printf("ns=%02o\n", newstate);
506fc377
EA
537 state = newstate & TYPE;
538 if (bitset(M, newstate))
539 c = NOCHAR;
540 if (bitset(B, newstate))
d6a28dd8 541 break;
b3cbe40f 542 }
d6a28dd8
EA
543
544 /* new token */
506fc377 545 if (tok != q)
2a119b55 546 {
506fc377 547 *q++ = '\0';
506fc377 548 if (tTd(22, 36))
b3cbe40f 549 {
506fc377
EA
550 printf("tok=");
551 xputs(tok);
03388044 552 (void) putchar('\n');
b3cbe40f 553 }
506fc377 554 if (avp >= &av[MAXATOM])
b3cbe40f 555 {
b6edea3d 556 syserr("553 prescan: too many tokens");
9e2cf26f
EA
557 if (delimptr != NULL)
558 *delimptr = p;
fba945cd 559 CurEnv->e_to = saveto;
506fc377 560 return (NULL);
b3cbe40f 561 }
506fc377 562 *avp++ = tok;
b3cbe40f 563 }
e93460a9 564 } while (c != '\0' && (c != delim || anglecnt > 0));
d6a28dd8 565 *avp = NULL;
9e2cf26f
EA
566 p--;
567 if (delimptr != NULL)
568 *delimptr = p;
97a521ff
EA
569 if (tTd(22, 12))
570 {
571 printf("prescan==>");
572 printav(av);
573 }
fba945cd 574 CurEnv->e_to = saveto;
6cb17374
EA
575 if (av[0] == NULL)
576 return (NULL);
1c4d5753 577 return (av);
d6a28dd8
EA
578}
579\f/*
580** TOKTYPE -- return token type
581**
582** Parameters:
583** c -- the character in question.
584**
585** Returns:
586** Its type.
587**
588** Side Effects:
589** none.
590*/
b3cbe40f 591
fd6cfc53 592static int
d6a28dd8 593toktype(c)
2bee003d 594 register int c;
d6a28dd8 595{
05f1e115 596 static char buf[50];
e232ff47 597 static bool firstime = TRUE;
05f1e115 598
e232ff47 599 if (firstime)
05f1e115 600 {
e232ff47 601 firstime = FALSE;
2bee003d 602 expand("\201o", buf, &buf[sizeof buf - 1], CurEnv);
f9566d23 603 (void) strcat(buf, DELIMCHARS);
05f1e115 604 }
2bee003d 605 c &= 0377;
c40bef63 606 if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
506fc377 607 return (ONE);
1ac3479f
EA
608 if (c == MACRODEXPAND)
609 return (ONE);
fd6cfc53
EA
610#ifdef MACVALUE
611 if (c == MACVALUE)
612 return (ONE);
613#endif /* MACVALUE */
506fc377
EA
614 if (c == '"')
615 return (QST);
2bee003d
EA
616 if ((c & 0340) == 0200)
617 return (OPR);
cdb17311 618 if (!isascii(c))
506fc377
EA
619 return (ATM);
620 if (isspace(c) || c == ')')
621 return (SPC);
2bee003d 622 if (strchr(buf, c) != NULL)
506fc377
EA
623 return (OPR);
624 return (ATM);
d6a28dd8
EA
625}
626\f/*
627** REWRITE -- apply rewrite rules to token vector.
628**
2258fda6
EA
629** This routine is an ordered production system. Each rewrite
630** rule has a LHS (called the pattern) and a RHS (called the
631** rewrite); 'rwr' points the the current rewrite rule.
632**
633** For each rewrite rule, 'avp' points the address vector we
634** are trying to match against, and 'pvp' points to the pattern.
792a6b53 635** If pvp points to a special match value (MATCHZANY, MATCHANY,
9f39d7cd
EA
636** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
637** matched is saved away in the match vector (pointed to by 'mvp').
2258fda6
EA
638**
639** When a match between avp & pvp does not match, we try to
9f39d7cd 640** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
2258fda6 641** we must also back out the match in mvp. If we reach a
792a6b53
EA
642** MATCHANY or MATCHZANY we just extend the match and start
643** over again.
2258fda6
EA
644**
645** When we finally match, we rewrite the address vector
646** and try over again.
647**
d6a28dd8
EA
648** Parameters:
649** pvp -- pointer to token vector.
1ac3479f
EA
650** ruleset -- the ruleset to use for rewriting.
651** e -- the current envelope.
d6a28dd8
EA
652**
653** Returns:
d1db7a89
EA
654** A status code. If EX_TEMPFAIL, higher level code should
655** attempt recovery.
d6a28dd8
EA
656**
657** Side Effects:
658** pvp is modified.
659*/
660
fd6cfc53
EA
661# define OP_NONZLEN 00001
662# define OP_VARLEN 00002
663# define OP_CLASS 00004
664# define OP_EXACT 00010
665
d6a28dd8
EA
666struct match
667{
54e8e18d
EA
668 char **first; /* first token matched */
669 char **last; /* last token matched */
9d6aecfd 670 char **pattern; /* pointer to pattern */
fd6cfc53
EA
671 char **source; /* left hand source operand */
672 char flags; /* attributes of this operator */
d6a28dd8
EA
673};
674
54e8e18d 675# define MAXMATCH 9 /* max params per rewrite */
fd6cfc53
EA
676# define MAX_CONTROL ' '
677
678static char control_opts[MAX_CONTROL];
679
d1db7a89 680int
fd6cfc53
EA
681static char control_init_data[] = {
682 MATCHZANY, OP_VARLEN,
683 MATCHONE, OP_NONZLEN,
684 MATCHANY, OP_VARLEN|OP_NONZLEN,
685#ifdef MACVALUE
686 MACVALUE, OP_EXACT,
687#endif /* MACVALUE */
688 MATCHNCLASS, OP_NONZLEN,
689 MATCHCLASS, OP_NONZLEN|OP_VARLEN|OP_CLASS,
fd6cfc53 690};
d6a28dd8 691
fd6cfc53 692static int nrw;
d6a28dd8 693
fd6cfc53 694void
1ac3479f 695rewrite(pvp, ruleset, e)
d6a28dd8 696 char **pvp;
f65e7ded 697 int ruleset;
1ac3479f 698 register ENVELOPE *e;
fd6cfc53
EA
699{
700 nrw = 0;
701 _rewrite(pvp, ruleset);
702}
703
704static void
705_rewrite(pvp, ruleset)
706 char **pvp;
707 int ruleset;
d6a28dd8
EA
708{
709 register char *ap; /* address pointer */
710 register char *rp; /* rewrite pointer */
711 register char **avp; /* address vector pointer */
712 register char **rvp; /* rewrite vector pointer */
792a6b53
EA
713 register struct match *mlp; /* cur ptr into mlist */
714 register struct rewrite *rwr; /* pointer to current rewrite rule */
75df37ee 715 int ruleno; /* current rule number */
d1db7a89 716 int rstat = EX_OK; /* return status */
41173b8f
EA
717 int subr; /* subroutine number if >= 0 */
718 bool dolookup; /* do host aliasing */
d6a28dd8 719 char *npvp[MAXATOM+1]; /* temporary space for rebuild */
fd6cfc53
EA
720 char tokbuf[MAXNAME+1]; /* for concatenated class tokens */
721 int nloops, nmatches = 0; /* for looping rule checks */
722 struct rewrite *prev_rwr; /* pointer to previous rewrite rule */
723 struct match mlist[MAXMATCH+1]; /* stores match on LHS */
724 struct match *old_mlp; /* to save our place */
725 bool extend_match; /* extend existing match during backup */
726
75f95954 727 if (OpMode == MD_TEST || tTd(21, 2))
d6a28dd8 728 {
b726abe3 729 printf("rewrite: ruleset %2d input:", ruleset);
fd6cfc53 730 printcav(pvp);
d6a28dd8 731 }
f62b79ae
EA
732 if (ruleset < 0 || ruleset >= MAXRWSETS)
733 {
b6edea3d 734 syserr("554 rewrite: illegal ruleset number %d", ruleset);
d1db7a89 735 return EX_CONFIG;
f62b79ae 736 }
e93460a9 737 if (pvp == NULL)
d1db7a89 738 return EX_USAGE;
d6a28dd8 739
fd6cfc53
EA
740 if (++nrw > 100)
741 {
742 char buf[MAXLINE];
743
744 buf[0] = buf[MAXLINE-1] = 0;
745 while (*pvp)
746 (void) strncat(buf, *pvp++, sizeof buf);
747 syserr("address causes rewrite loop: <%s>", buf);
748 return;
749 }
750
751 /* Be sure to recognize first rule as new */
752 prev_rwr = NULL;
753
d6a28dd8 754 /*
fd6cfc53 755 ** Run through the list of rewrite rules, applying any that match.
d6a28dd8
EA
756 */
757
75df37ee 758 ruleno = 1;
f65e7ded 759 for (rwr = RewriteRules[ruleset]; rwr != NULL; )
d6a28dd8 760 {
72f91c2e
EA
761 int loopcount = 0;
762
9678c96d 763 if (tTd(21, 12))
d6a28dd8 764 {
b00e6882 765 printf("-----trying rule:");
fd6cfc53
EA
766 printcav(rwr->r_lhs);
767 }
768
769 /*
770 ** Set up the match list. This is done once for each
771 ** rule. If a rule is used repeatedly, the list need not
772 ** be set up the next time.
773 */
774
775 if (rwr != prev_rwr)
776 {
777 prev_rwr = rwr;
778 for (rvp = rwr->r_lhs, mlp = mlist;
779 *rvp && (mlp < &mlist[MAXMATCH]); rvp++)
780 {
781 mlp->flags = ((unsigned char) **rvp >= MAX_CONTROL) ?
782 0 : control_opts[**rvp] ;
783 if (mlp->flags)
784 {
785 mlp->source = rvp;
786 mlp++;
787 }
788 }
789 if (*rvp)
790 {
791 syserr("Too many variables on LHS in ruleset %d", ruleset);
792 return;
793 }
794 mlp->source = rvp;
795
796 /* Make sure end marker is initialized */
797 mlp->flags = 0;
d6a28dd8 798 }
ecf90b7d 799
d6a28dd8 800 /* try to match on this rule */
54e8e18d 801 mlp = mlist;
fd6cfc53 802
792a6b53
EA
803 rvp = rwr->r_lhs;
804 avp = pvp;
fd6cfc53
EA
805 nloops = 0;
806 extend_match = FALSE;
807
75df37ee 808 if (++loopcount > 100)
b3cbe40f 809 {
fd6cfc53
EA
810 if (nloops++ > 400)
811 {
812 syserr("Looping on ruleset %d, rule %d",
813 ruleset, rwr-RewriteRules[ruleset]);
814 mlp = mlist - 1; /* force rule failure */
815 break;
816 }
75df37ee
EA
817 syserr("554 Infinite loop in ruleset %d, rule %d",
818 ruleset, ruleno);
819 if (tTd(21, 1))
72f91c2e 820 {
f62b79ae
EA
821 printf("workspace: ");
822 printav(pvp);
72f91c2e 823 }
75df37ee
EA
824 break;
825 }
826
827 while ((ap = *avp) != NULL || *rvp != NULL)
828 {
d6a28dd8 829 rp = *rvp;
fd6cfc53 830
792a6b53
EA
831 if (tTd(21, 35))
832 {
9d6aecfd 833 printf("ADVANCE rp=");
792a6b53 834 xputs(rp);
5a1d901b 835 printf(", ap=");
511c3bad 836 xputs(ap);
b00e6882 837 printf("\n");
792a6b53 838 }
fd6cfc53
EA
839
840 if (extend_match)
841 extend_match = FALSE;
842 else
b3cbe40f 843 {
fd6cfc53
EA
844 mlp->first = avp;
845 mlp->last = mlp->flags == 0 || (mlp->flags & OP_NONZLEN) ?
846 avp + 1 : avp;
847 }
848
849 if (rp == NULL)
d6a28dd8 850 /* end-of-pattern before end-of-address */
792a6b53 851 goto backup;
fd6cfc53
EA
852
853 /* Premature end of address */
854 if (ap == NULL && avp != mlp->last)
855 goto backup;
856
857 /*
858 ** Simplest case - exact token comparison between
859 ** pattern and address. Such a match is not saved
860 ** in mlp.
861 */
862
863 if (rvp < mlp->source)
792a6b53 864 {
fd6cfc53
EA
865 if (ap == NULL || strcasecmp(ap, rp))
866 goto backup;
867 rvp++;
868 avp++;
869 continue;
d6a28dd8
EA
870 }
871
fd6cfc53
EA
872#ifdef MACVALUE
873 /*
874 ** This is special case handled. The match is exact,
875 ** but might span zero or more address tokens. The
876 ** result is saved in mlp.
877 */
878
879 if (*rp == MACVALUE)
d6a28dd8 880 {
fd6cfc53
EA
881 int len;
882 rp = macvalue(rp[1], CurEnv);
13834bfc 883
fd6cfc53
EA
884 if (rp)
885 while (*rp)
886 {
887 if (*avp == NULL || strncasecmp(rp,*avp,len = strlen(*avp)))
888 goto backup;
889 rp += len;
890 avp++;
891 }
892 mlp->last = avp;
893 rvp++;
894 mlp++;
895 continue;
896 }
897#endif /* MACVALUE */
898
899 /*
900 ** All other matches are saved in mlp. Initially
901 ** assume they match at the shortest possible length
902 ** for this pattern. Variable patterns will be
903 ** extended later as needed.
904 */
905
906 /* Fixed length first */
907 if (!(mlp->flags & OP_VARLEN))
908 {
909 switch (*rp)
9f39d7cd 910 {
fd6cfc53 911 break;
fd6cfc53 912 }
792a6b53 913
fd6cfc53
EA
914 avp = mlp->last;
915 rvp++;
54e8e18d 916 mlp++;
1bca74dd
EA
917 break;
918
a42056e7 919 case MATCHZERO:
1bca74dd 920 /* match zero tokens */
fd6cfc53
EA
921 continue;
922 }
923
1ac3479f
EA
924 case MACRODEXPAND:
925 /*
926 ** Match against run-time macro.
927 ** This algorithm is broken for the
928 ** general case (no recursive macros,
929 ** improper tokenization) but should
930 ** work for the usual cases.
931 */
932
933 ap = macvalue(rp[1], e);
934 mlp->first = avp;
935 if (tTd(21, 2))
936 printf("rewrite: LHS $&%c => \"%s\"\n",
937 rp[1],
938 ap == NULL ? "(NULL)" : ap);
939
940 if (ap == NULL)
941 break;
54d35e43 942 while (*ap != '\0')
1ac3479f
EA
943 {
944 if (*avp == NULL ||
945 strncasecmp(ap, *avp, strlen(*avp)) != 0)
946 {
947 /* no match */
948 avp = mlp->first;
949 goto backup;
950 }
951 ap += strlen(*avp++);
952 }
953
954 /* match */
955 break;
956
fd6cfc53
EA
957 /*
958 ** We now have a variable length item. It could
959 ** be $+ or $* in which case no special checking
960 ** is needed. But a class match such as $=x must
961 ** be verified.
962 **
963 ** As a speedup, if a variable length item is
964 ** followed by a plain character token, we initially
965 ** extend the match to the first such token we find.
966 ** If the required character token cannot be found,
967 ** we fail the match at this point.
968 */
969
970 avp = mlp->last;
971
972 /* If next token is char token */
973 if (&rvp[1] < mlp[1].source)
974 {
975 while (*avp && strcasecmp(*avp, rvp[1]))
976 avp++;
977
978 /*
979 ** If we can't find the proper ending token,
980 ** leave avp point to NULL. This indicates
981 ** we have run out of address tokens. It is
982 ** pointless to advance the beginning of this
983 ** match and retry.
984 */
13834bfc 985
fd6cfc53 986 if (*avp == NULL)
792a6b53 987 goto backup;
fd6cfc53
EA
988 mlp->last = avp;
989 }
990 else if (rvp[1] == NULL)
991 /* next token is end of address */
992 {
993 while (*avp)
994 avp++;
995 mlp->last = avp;
996 }
997
998 if (mlp->flags & OP_CLASS)
999 {
1000 register char *cp = tokbuf;
1001
1002 avp = mlp->first;
1003 strcpy(cp, *avp);
54e8e18d 1004 avp++;
fd6cfc53
EA
1005 for (;;)
1006 {
1007 while (avp < mlp->last)
1008 {
1009 while (*cp)
1010 cp++;
1011 strcpy(cp, *avp);
1012 avp++;
1013 }
1014 switch (*rp)
1015 {
1016 register STAB *s;
1017
1018 case MATCHCLASS:
1019 s = stab(tokbuf, ST_CLASS, ST_FIND);
1020 if (s != NULL && bitnset(rp[1], s->s_class))
1021 goto have_match;
1022 break;
fd6cfc53
EA
1023 }
1024
1025 /*
1026 ** Class match initially failed.
1027 ** Extend the tentative match.
1028 ** Again, if followed by a character
1029 ** token, extend all the way to that
1030 ** token before checking.
1031 */
1032
1033 if (*avp)
1034 {
1035 (mlp->last)++;
1036 if (&rvp[1] < mlp[1].source)
1037 {
1038 while (*(mlp->last) && strcasecmp(*(mlp->last), rvp[1]))
1039 (mlp->last)++;
1040 if (*(mlp->last) == NULL)
1041 avp = mlp->last;
1042 }
1043 }
1044 if (*avp == NULL)
1045 {
1046 /*
1047 ** We could not find the
1048 ** ending token. But we had
1049 ** found ending tokens before.
1050 ** A match is still plausible
1051 ** if the start of the
1052 ** tentative match is advanced.
1053 ** Hence we must not leave avp
1054 ** pointing to NULL.
1055 */
1056 avp = mlp->first;
1057 goto backup;
1058 }
1059 }
d6a28dd8
EA
1060 }
1061
fd6cfc53 1062 have_match:
d6a28dd8 1063 rvp++;
fd6cfc53 1064 mlp++;
d6a28dd8
EA
1065 continue;
1066
fd6cfc53
EA
1067backup:
1068 /* We failed to match. mlp marks point of failure */
1069
1070 /*
1071 ** There is a special case when we have exhausted
1072 ** the address, but have not exhausted the pattern.
1073 ** Under normal circumstances we could consider the
1074 ** failure permanent, since extending the number of
1075 ** address tokens matched by a '$+' or a '$*' will
1076 ** only worsen the situation.
1077 **
1078 ** There is an exception, however. It is possible
1079 ** that we have matched a class token, say '$=x',
1080 ** with three or more tokens. Extending a '$+' say,
1081 ** which precedes the '$=x' will move the beginning
1082 ** of the '$=x' match to the right, but it might match
1083 ** a smaller number of tokens then, possibly
1084 ** correcting the mismatch.
1085 **
1086 ** Thus in this case we initially back up to the
1087 ** $=x which matches three or more tokens.
1088 */
1089
1090 if (*avp == NULL)
d6a28dd8 1091 {
9d6aecfd 1092 rvp = mlp->pattern;
fd6cfc53 1093 while (--mlp > mlist)
54e8e18d 1094 {
fd6cfc53
EA
1095 if ((mlp->flags & OP_CLASS) &&
1096 mlp->last > 2 + mlp->first)
1097 break;
d6a28dd8
EA
1098 }
1099 }
1100
fd6cfc53
EA
1101 /*
1102 ** Now backup till we find a match with a pattern
1103 ** whose length is extendable, and extend that.
1104 */
1105
1106 mlp--;
1107 while (mlp >= mlist && !(mlp->flags & OP_VARLEN))
1108 mlp--;
1109
1110 /* Total failure to match */
1111 if (mlp < mlist)
d6a28dd8 1112 break;
fd6cfc53
EA
1113
1114 avp = ++(mlp->last);
1115 rvp = mlp->source;
1116
1117 /*
1118 ** We have found a backup point. Normally we would
1119 ** increase the matched amount by one token, and
1120 ** continue from the next item in the pattern. But
1121 ** there are two special cases. If this is a
1122 ** class-type match (OP_CLASS), we must test the
1123 ** validity of the extended match. If this pattern
1124 ** item is directly followed by a character token, it
1125 ** is worth going back and locating the next such
1126 ** character token before we continue on.
1127 */
1128 if ((mlp->flags & OP_CLASS) || (&rvp[1] < mlp[1].source))
1129 {
1130 avp = mlp->first;
1131 extend_match = TRUE;
1132 }
1133 else
1134 {
1135 mlp++;
1136 rvp++;
b3cbe40f 1137 }
b3cbe40f 1138 }
d6a28dd8
EA
1139
1140 /*
fd6cfc53 1141 ** See if we successfully matched.
d6a28dd8
EA
1142 */
1143
fd6cfc53 1144 if (mlp < mlist)
d6a28dd8 1145 {
7338e3d4
EA
1146 if (tTd(21, 10))
1147 printf("----- rule fails\n");
7338e3d4 1148 rwr = rwr->r_next;
75df37ee 1149 ruleno++;
fd6cfc53
EA
1150 nmatches = 0;
1151 continue;
1152 }
1153
1154 if (nmatches++ > 200)
1155 {
1156 syserr("Loop in ruleset %d, rule %d (too many matches)",
1157 ruleset, rwr - RewriteRules[ruleset]);
1158 rwr = rwr->r_next;
1159 nmatches = 0;
7338e3d4
EA
1160 continue;
1161 }
d6a28dd8 1162
7338e3d4 1163 rvp = rwr->r_rhs;
7338e3d4
EA
1164 if (tTd(21, 12))
1165 {
1166 printf("-----rule matches:");
fd6cfc53 1167 printcav(rvp);
7338e3d4 1168 }
7338e3d4
EA
1169
1170 rp = *rvp;
2bee003d 1171 if ((*rp & 0377) == CANONUSER)
7338e3d4
EA
1172 {
1173 rvp++;
1174 rwr = rwr->r_next;
75df37ee 1175 ruleno++;
fd6cfc53 1176 nmatches = 0;
7338e3d4 1177 }
2bee003d 1178 else if ((*rp & 0377) == CANONHOST)
7338e3d4
EA
1179 {
1180 rvp++;
1181 rwr = NULL;
1182 }
2bee003d 1183 else if ((*rp & 0377) == CANONNET)
7338e3d4
EA
1184 rwr = NULL;
1185
1186 /* substitute */
41173b8f 1187 dolookup = FALSE;
7338e3d4
EA
1188 for (avp = npvp; *rvp != NULL; rvp++)
1189 {
1190 register struct match *m;
1191 register char **pp;
792a6b53 1192
7338e3d4 1193 rp = *rvp;
41173b8f
EA
1194
1195 /* check to see if we should do a lookup */
1196 if (*rp == MATCHLOOKUP)
1197 dolookup = TRUE;
1198
1199 /* see if there is substitution here */
fd6cfc53 1200 if (*rp == MATCHREPL && rp[1] >= '1' && rp[1] <= '9')
d6a28dd8 1201 {
217a0102
EA
1202 /* substitute from LHS */
1203 m = &mlist[rp[1] - '1'];
f62b79ae 1204 if (m < mlist || m >= mlp)
d6a28dd8 1205 {
41173b8f 1206 toolong:
fd6cfc53
EA
1207 syserr("rewrite: ruleset %d: replacement #%c out of bounds",
1208 ruleset, rp[1]);
d1db7a89 1209 return EX_CONFIG;
7338e3d4 1210 }
217a0102
EA
1211 if (tTd(21, 15))
1212 {
1213 printf("$%c:", rp[1]);
1214 pp = m->first;
fd6cfc53 1215 while (pp < m->last)
217a0102
EA
1216 {
1217 printf(" %x=\"", *pp);
1218 (void) fflush(stdout);
1219 printf("%s\"", *pp++);
1220 }
1221 printf("\n");
1222 }
7338e3d4 1223 pp = m->first;
fd6cfc53 1224 while (pp < m->last)
e96bade8 1225 {
217a0102 1226 if (avp >= &npvp[MAXATOM])
fd6cfc53 1227 goto toolong;
217a0102 1228 *avp++ = *pp++;
e96bade8 1229 }
d6a28dd8 1230 }
217a0102 1231 else
45ecb1bf 1232 {
217a0102 1233 /* vanilla replacement */
7338e3d4 1234 if (avp >= &npvp[MAXATOM])
41173b8f 1235 goto toolong;
fd6cfc53
EA
1236#ifdef MACVALUE
1237 if (*rp == MACVALUE)
1238 {
1239 char *p = macvalue(rp[1], CurEnv);
1240
1241 if (tTd(21, 2))
1242 printf("expanding runtime macro '%c' to \"%s\"\n",
1243 rp[1], p ? p : "(null)");
1244 if (p)
1245 *avp++ = p;
1246 }
1247 else
1248#endif /* MACVALUE */
1249 *avp++ = rp;
45ecb1bf 1250 }
7338e3d4
EA
1251 }
1252 *avp++ = NULL;
41173b8f 1253
217a0102 1254 /*
42fa5d67 1255 ** Check for any hostname/keyword lookups.
217a0102
EA
1256 */
1257
1258 for (rvp = npvp; *rvp != NULL; rvp++)
1259 {
fd6cfc53 1260 char **hbrvp, **ubrvp;
217a0102
EA
1261 char **xpvp;
1262 int trsize;
42fa5d67
EA
1263 char *replac;
1264 int endtoken;
1265 STAB *map;
1266 char *mapname;
1267 char **key_rvp;
1268 char **arg_rvp;
1269 char **default_rvp;
fd6cfc53 1270 char hbuf[MAXNAME + 1], ubuf[MAXNAME + 1];
217a0102 1271 char *pvpb1[MAXATOM + 1];
5a4c03c6 1272 char *argvect[10];
7ac75266 1273 char pvpbuf[PSBUFSIZE];
fd6cfc53
EA
1274 bool match, defaultpart;
1275 char begintype;
1276 char db = '\0';
217a0102 1277
2bee003d
EA
1278 if ((**rvp & 0377) != HOSTBEGIN &&
1279 (**rvp & 0377) != LOOKUPBEGIN)
217a0102
EA
1280 continue;
1281
1282 /*
42fa5d67 1283 ** Got a hostname/keyword lookup.
217a0102
EA
1284 **
1285 ** This could be optimized fairly easily.
1286 */
1287
fd6cfc53 1288 begintype = **rvp;
217a0102 1289 hbrvp = rvp;
fd6cfc53 1290 ubrvp = NULL;
2bee003d 1291 if ((**rvp & 0377) == HOSTBEGIN)
42fa5d67
EA
1292 {
1293 endtoken = HOSTEND;
1294 mapname = "host";
1295 }
1296 else
1297 {
1298 endtoken = LOOKUPEND;
1299 mapname = *++rvp;
1300 }
1301 map = stab(mapname, ST_MAP, ST_FIND);
1302 if (map == NULL)
b6edea3d 1303 syserr("554 rewrite: map %s not found", mapname);
217a0102
EA
1304
1305 /* extract the match part */
42fa5d67 1306 key_rvp = ++rvp;
5a4c03c6
EA
1307 default_rvp = NULL;
1308 arg_rvp = argvect;
1309 xpvp = NULL;
1310 replac = pvpbuf;
2bee003d 1311 while (*rvp != NULL && (**rvp & 0377) != endtoken)
42fa5d67 1312 {
2bee003d 1313 int nodetype = **rvp & 0377;
5a4c03c6
EA
1314
1315 if (nodetype != CANONHOST && nodetype != CANONUSER)
1316 {
1317 rvp++;
1318 continue;
1319 }
1320
1321 *rvp++ = NULL;
1322
1323 if (xpvp != NULL)
1324 {
4fcb2bfa 1325 cataddr(xpvp, NULL, replac,
1c7897ef
EA
1326 &pvpbuf[sizeof pvpbuf] - replac,
1327 '\0');
5a4c03c6
EA
1328 *++arg_rvp = replac;
1329 replac += strlen(replac) + 1;
1330 xpvp = NULL;
1331 }
1332 switch (nodetype)
42fa5d67
EA
1333 {
1334 case CANONHOST:
5a4c03c6 1335 xpvp = rvp;
42fa5d67
EA
1336 break;
1337
1338 case CANONUSER:
42fa5d67
EA
1339 default_rvp = rvp;
1340 break;
42fa5d67
EA
1341 }
1342 }
217a0102
EA
1343 if (*rvp != NULL)
1344 *rvp++ = NULL;
5a4c03c6
EA
1345 if (xpvp != NULL)
1346 {
4fcb2bfa 1347 cataddr(xpvp, NULL, replac,
1c7897ef
EA
1348 &pvpbuf[sizeof pvpbuf] - replac,
1349 '\0');
5a4c03c6
EA
1350 *++arg_rvp = replac;
1351 }
1352 *++arg_rvp = NULL;
217a0102
EA
1353
1354 /* save the remainder of the input string */
1355 trsize = (int) (avp - rvp + 1) * sizeof *rvp;
1356 bcopy((char *) rvp, (char *) pvpb1, trsize);
1357
217a0102 1358 /* append it to the token list */
fd6cfc53
EA
1359 for (avp = hbrvp; *xpvp != NULL; xpvp++)
1360 {
7ac75266 1361 *avp++ = newstr(*xpvp);
c0ef545b 1362 if (avp >= &npvp[MAXATOM])
217a0102 1363 goto toolong;
fd6cfc53 1364 }
7ac75266 1365 }
fd6cfc53
EA
1366 else
1367 avp = hbrvp;
217a0102
EA
1368
1369 /* restore the old trailing information */
fd6cfc53
EA
1370 rvp = avp - 1;
1371 for (xpvp = pvpb1; *xpvp != NULL; xpvp++)
1372 {
c40bef63 1373 if (defaultpart && **xpvp == HOSTEND)
fd6cfc53
EA
1374 {
1375 defaultpart = FALSE;
1376 rvp = avp - 1;
1377 }
1378 else if (!defaultpart || !match)
1379 *avp++ = *xpvp;
c0ef545b 1380 if (avp >= &npvp[MAXATOM])
217a0102 1381 goto toolong;
fd6cfc53
EA
1382 }
1383 *avp++ = NULL;
7ac75266 1384
fd6cfc53 1385 /*break;*/
217a0102
EA
1386 }
1387
1388 /*
1389 ** Check for subroutine calls.
fd6cfc53 1390 ** Then copy vector back into original space.
217a0102
EA
1391 */
1392
fd6cfc53 1393 callsubr(npvp);
217a0102 1394
fd6cfc53 1395 for (avp = npvp; *avp++ != NULL;);
41173b8f
EA
1396 subr = atoi(*++rvp);
1397 rvp++;
fd6cfc53 1398
41173b8f
EA
1399 else
1400 subr = -1;
1401
1402 /*
1403 ** Copy result back to original string.
1404 */
1405
1406 for (avp = pvp; *rvp != NULL; rvp++)
1407 *avp++ = *rvp;
1408 *avp = NULL;
1409
1410 /*
1411 ** If this specified a subroutine, call it.
1412 */
1413
1414 if (subr >= 0)
1415 {
1416# ifdef DEBUG
1417 if (tTd(21, 3))
1418 printf("-----callsubr %s\n", subr);
1419# endif DEBUG
1420 rewrite(pvp, subr);
1421 }
1422
1423 /*
1424 ** Done with rewriting this pass.
1425 */
1426
7338e3d4
EA
1427 if (tTd(21, 4))
1428 {
1429 printf("rewritten as:");
fd6cfc53 1430 printcav(pvp);
d6a28dd8 1431 }
b3cbe40f 1432 }
b00e6882 1433
75f95954 1434 if (OpMode == MD_TEST || tTd(21, 2))
b00e6882 1435 {
b726abe3 1436 printf("rewrite: ruleset %2d returns:", ruleset);
fd6cfc53 1437 printcav(pvp);
b00e6882 1438 }
d1db7a89
EA
1439
1440 return rstat;
d6a28dd8
EA
1441}
1442\f/*
fd6cfc53
EA
1443** CALLSUBR -- call subroutines in rewrite vector
1444**
1445** Parameters:
1446** pvp -- pointer to token vector.
1447**
1448** Returns:
1449** none.
1450**
1451** Side Effects:
1452** pvp is modified.
1453*/
1454
1455static void
1456callsubr(pvp)
1457 char **pvp;
1458{
1459 char **rvp;
1460 int subr;
1461
1462 for (; *pvp != NULL; pvp++)
1463 if (**pvp == CALLSUBR && pvp[1] != NULL && isdigit(pvp[1][0]))
1464 {
1465 subr = atoi(pvp[1]);
1466
1467 if (tTd(21, 3))
1468 printf("-----callsubr %d\n", subr);
1469
1470 /*
1471 ** Take care of possible inner calls.
1472 */
1473 callsubr(pvp+2);
1474
1475 /*
1476 ** Move vector up over calling opcode.
1477 */
1478 for (rvp = pvp+2; *rvp != NULL; rvp++)
1479 rvp[-2] = rvp[0];
1480 rvp[-2] = NULL;
1481
1482 /*
1483 ** Call inferior ruleset.
1484 */
1485 _rewrite(pvp, subr);
1486
1487 break;
1488 }
1489}
1490\f/*
d6a28dd8
EA
1491** BUILDADDR -- build address from token vector.
1492**
1493** Parameters:
1494** tv -- token vector.
1495** a -- pointer to address descriptor to fill.
1496** If NULL, one will be allocated.
28f94061
EA
1497** flags -- info regarding whether this is a sender or
1498** a recipient.
35b698c6 1499** e -- the current envelope.
d6a28dd8
EA
1500**
1501** Returns:
fe43b434
EA
1502** NULL if there was an error.
1503** 'a' otherwise.
d6a28dd8
EA
1504**
1505** Side Effects:
1506** fills in 'a'
1507*/
1508
c796f255
EA
1509struct errcodes
1510{
1511 char *ec_name; /* name of error code */
1512 int ec_code; /* numeric code */
1513} ErrorCodes[] =
1514{
1515 "usage", EX_USAGE,
1516 "nouser", EX_NOUSER,
1517 "nohost", EX_NOHOST,
1518 "unavailable", EX_UNAVAILABLE,
1519 "software", EX_SOFTWARE,
1520 "tempfail", EX_TEMPFAIL,
1521 "protocol", EX_PROTOCOL,
1522#ifdef EX_CONFIG
1523 "config", EX_CONFIG,
1524#endif
1525 NULL, EX_UNAVAILABLE,
1526};
1527
fd6cfc53 1528static ADDRESS *
28f94061 1529buildaddr(tv, a, flags, e)
d6a28dd8
EA
1530 register char **tv;
1531 register ADDRESS *a;
28f94061 1532 int flags;
35b698c6 1533 register ENVELOPE *e;
d6a28dd8 1534{
d6a28dd8
EA
1535 struct mailer **mp;
1536 register struct mailer *m;
51d9cc47
EA
1537 char *bp;
1538 int spaceleft;
9577b1df
EA
1539 static MAILER errormailer;
1540 static char *errorargv[] = { "ERROR", NULL };
98e5062b 1541 static char buf[MAXNAME];
d6a28dd8
EA
1542
1543 if (a == NULL)
1544 a = (ADDRESS *) xalloc(sizeof *a);
abae7b2d 1545 clear((char *) a, sizeof *a);
d6a28dd8
EA
1546
1547 /* figure out what net/mailer to use */
fd6cfc53 1548 if (*tv == NULL || **tv != CANONNET)
fe43b434 1549 {
b6edea3d 1550 syserr("554 buildaddr: no net");
9577b1df
EA
1551badaddr:
1552 a->q_flags |= QBADADDR;
1553 a->q_mailer = &errormailer;
1554 if (errormailer.m_name == NULL)
1555 {
1556 /* initialize the bogus mailer */
1557 errormailer.m_name = "*error*";
1558 errormailer.m_mailer = "ERROR";
1559 errormailer.m_argv = errorargv;
1560 }
1561 return a;
fe43b434 1562 }
d6a28dd8 1563 tv++;
bc854e30 1564 if (strcasecmp(*tv, "error") == 0)
fe43b434 1565 {
2bee003d 1566 if ((**++tv & 0377) == CANONHOST)
4fd73cf9 1567 {
c796f255
EA
1568 register struct errcodes *ep;
1569
2bee003d 1570 if (isascii(**++tv) && isdigit(**tv))
c796f255
EA
1571 {
1572 setstat(atoi(*tv));
1573 }
1574 else
1575 {
1576 for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
1577 if (strcasecmp(ep->ec_name, *tv) == 0)
1578 break;
1579 setstat(ep->ec_code);
1580 }
4fd73cf9
EA
1581 tv++;
1582 }
fd6cfc53
EA
1583 buf[0] = '\0';
1584 for (; (*tv != NULL) && (**tv != CANONUSER); tv++)
1585 {
1586 if (buf[0] != '\0')
1587 (void) strcat(buf, " ");
1588 (void) strcat(buf, *tv);
1589 }
2bee003d 1590 if ((**tv & 0377) != CANONUSER)
b6edea3d 1591 syserr("554 buildaddr: error: no user");
4fcb2bfa 1592 cataddr(++tv, NULL, buf, sizeof buf, ' ');
1c7897ef 1593 stripquotes(buf);
fd6cfc53
EA
1594#ifdef LOG
1595 if (LogLevel > 8)
1596 syslog (LOG_DEBUG, "%s: Trace: $#ERROR $: %s",
1597 CurEnv->e_id, buf);
1598#endif /* LOG */
f51fdacb 1599 usrerr("%s", buf);
9577b1df 1600 goto badaddr;
fe43b434 1601 }
98e5062b 1602
179c1218 1603 for (mp = Mailer; (m = *mp++) != NULL; )
d6a28dd8 1604 {
bc854e30 1605 if (strcasecmp(m->m_name, *tv) == 0)
d6a28dd8
EA
1606 break;
1607 }
1608 if (m == NULL)
fe43b434 1609 {
b6edea3d 1610 syserr("554 buildaddr: unknown mailer %s", *tv);
9577b1df 1611 goto badaddr;
fe43b434 1612 }
179c1218 1613 a->q_mailer = m;
d6a28dd8
EA
1614
1615 /* figure out what host (if any) */
fd6cfc53 1616 if (**++tv != CANONHOST)
d6a28dd8 1617 {
6b3bbc1b
EA
1618 a->q_host = NULL;
1619 }
1620 else
1621 {
fd6cfc53
EA
1622 else
1623 a->q_host = NULL;
1624 }
1625 else
1626 {
fd6cfc53
EA
1627 while (*++tv != NULL && **tv != CANONUSER)
1628 (void) strcat(buf, *tv);
35cc3fad 1629 a->q_host = newstr(buf);
d6a28dd8 1630 }
d6a28dd8
EA
1631
1632 /* figure out the user */
2bee003d 1633 if (*tv == NULL || (**tv & 0377) != CANONUSER)
fe43b434 1634 {
b6edea3d 1635 syserr("554 buildaddr: no user");
9577b1df 1636 goto badaddr;
fe43b434 1637 }
98e5062b
EA
1638 tv++;
1639
1640 /* do special mapping for local mailer */
1641 if (m == LocalMailer && *tv != NULL)
1642 {
7e70d3c8
EA
1643 register char *p = *tv;
1644
1645 if (*p == '"')
1646 p++;
1647 if (*p == '|')
98e5062b 1648 a->q_mailer = m = ProgMailer;
7e70d3c8 1649 else if (*p == '/')
98e5062b 1650 a->q_mailer = m = FileMailer;
7e70d3c8 1651 else if (*p == ':')
98e5062b
EA
1652 {
1653 /* may be :include: */
4fcb2bfa 1654 cataddr(tv, NULL, buf, sizeof buf, '\0');
51d9cc47
EA
1655 stripquotes(buf);
1656 if (strncasecmp(buf, ":include:", 9) == 0)
1657 {
1658 /* if :include:, don't need further rewriting */
98e5062b 1659 a->q_mailer = m = InclMailer;
51d9cc47
EA
1660 a->q_user = &buf[9];
1661 return (a);
1662 }
98e5062b
EA
1663 }
1664 }
d13779b1 1665
51d9cc47
EA
1666 if (m == LocalMailer && *tv != NULL && strcmp(*tv, "@") == 0)
1667 {
1668 tv++;
1669 a->q_flags |= QNOTREMOTE;
1670 }
1671
c40bef63
EA
1672 if (m->m_r_rwset > 0)
1673 rewrite(tv, m->m_r_rwset);
d1db7a89 1674 (void) rewrite(tv, 4, e);
5108b0cc
EA
1675
1676 /* save the result for the command line/RCPT argument */
4fcb2bfa 1677 cataddr(tv, NULL, buf, sizeof buf, '\0');
d6a28dd8
EA
1678 a->q_user = buf;
1679
dd3ec3f5
EA
1680 /*
1681 ** Do mapping to lower case as requested by mailer
1682 */
1683
1684 if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
1685 makelower(a->q_host);
1686 if (!bitnset(M_USR_UPPER, m->m_flags))
1687 makelower(a->q_user);
1688
d6a28dd8
EA
1689 return (a);
1690}
9e3c0a28 1691\f/*
31b33174
EA
1692** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
1693**
1694** Parameters:
1695** pvp -- parameter vector to rebuild.
4fcb2bfa
EA
1696** evp -- last parameter to include. Can be NULL to
1697** use entire pvp.
31b33174
EA
1698** buf -- buffer to build the string into.
1699** sz -- size of buf.
1c7897ef
EA
1700** spacesub -- the space separator character; if null,
1701** use SpaceSub.
31b33174
EA
1702**
1703** Returns:
1704** none.
1705**
1706** Side Effects:
1707** Destroys buf.
1708*/
1709
fd6cfc53 1710void
4fcb2bfa 1711cataddr(pvp, evp, buf, sz, spacesub)
31b33174 1712 char **pvp;
4fcb2bfa 1713 char **evp;
31b33174
EA
1714 char *buf;
1715 register int sz;
1c7897ef 1716 char spacesub;
31b33174
EA
1717{
1718 bool oatomtok = FALSE;
fd6cfc53 1719 bool natomtok;
31b33174
EA
1720 register int i;
1721 register char *p;
1722
1c7897ef
EA
1723 if (spacesub == '\0')
1724 spacesub = SpaceSub;
1725
e93460a9
EA
1726 if (pvp == NULL)
1727 {
03388044 1728 (void) strcpy(buf, "");
e93460a9
EA
1729 return;
1730 }
31b33174 1731 p = buf;
89dd4c8f 1732 sz -= 2;
31b33174
EA
1733 while (*pvp != NULL && (i = strlen(*pvp)) < sz)
1734 {
506fc377 1735 natomtok = (toktype(**pvp) == ATM);
31b33174 1736 if (oatomtok && natomtok)
1c7897ef 1737 *p++ = spacesub;
31b33174
EA
1738 (void) strcpy(p, *pvp);
1739 oatomtok = natomtok;
1740 p += i;
89dd4c8f 1741 sz -= i + 1;
4fcb2bfa
EA
1742 if (pvp++ == evp)
1743 break;
31b33174
EA
1744 }
1745 *p = '\0';
1746}
1747\f/*
9e3c0a28
EA
1748** SAMEADDR -- Determine if two addresses are the same
1749**
1750** This is not just a straight comparison -- if the mailer doesn't
1751** care about the host we just ignore it, etc.
1752**
1753** Parameters:
1754** a, b -- pointers to the internal forms to compare.
9e3c0a28
EA
1755**
1756** Returns:
1757** TRUE -- they represent the same mailbox.
1758** FALSE -- they don't.
1759**
1760** Side Effects:
1761** none.
1762*/
1763
1764bool
7338e3d4 1765sameaddr(a, b)
9e3c0a28
EA
1766 register ADDRESS *a;
1767 register ADDRESS *b;
9e3c0a28
EA
1768{
1769 /* if they don't have the same mailer, forget it */
1770 if (a->q_mailer != b->q_mailer)
1771 return (FALSE);
1772
1773 /* if the user isn't the same, we can drop out */
fd6cfc53 1774 if (strcasecmp(a->q_user, b->q_user))
9e3c0a28
EA
1775 return (FALSE);
1776
4e535265
EA
1777 /* if we have good uids for both but the differ, these are different */
1778 if (bitset(QGOODUID, a->q_flags & b->q_flags) && a->q_uid != b->q_uid)
1779 return (FALSE);
1780
9e3c0a28 1781 /* otherwise compare hosts (but be careful for NULL ptrs) */
fa085f58
EA
1782 if (a->q_host == b->q_host)
1783 {
1784 /* probably both null pointers */
1785 return (TRUE);
1786 }
9e3c0a28 1787 if (a->q_host == NULL || b->q_host == NULL)
fa085f58
EA
1788 {
1789 /* only one is a null pointer */
9e3c0a28 1790 return (FALSE);
fa085f58 1791 }
fd6cfc53 1792 if (strcasecmp(a->q_host, b->q_host))
9e3c0a28
EA
1793 return (FALSE);
1794
1795 return (TRUE);
1796}
779597d8
EA
1797\f/*
1798** PRINTADDR -- print address (for debugging)
1799**
1800** Parameters:
1801** a -- the address to print
1802** follow -- follow the q_next chain.
1803**
1804** Returns:
1805** none.
1806**
1807** Side Effects:
1808** none.
1809*/
1810
fd6cfc53 1811void
779597d8
EA
1812printaddr(a, follow)
1813 register ADDRESS *a;
1814 bool follow;
1815{
d4f42161 1816 bool first = TRUE;
78bbbc48
EA
1817 register MAILER *m;
1818 MAILER pseudomailer;
d4f42161 1819
abae7b2d
EA
1820 static int indent;
1821 register int i;
1822
779597d8
EA
1823 while (a != NULL)
1824 {
d4f42161 1825 first = FALSE;
abae7b2d
EA
1826 for (i = indent; i > 0; i--)
1827 printf("\t");
331b7c9f 1828 printf("%x=", a);
29871fef 1829 (void) fflush(stdout);
78bbbc48
EA
1830
1831 /* find the mailer -- carefully */
1832 m = a->q_mailer;
1833 if (m == NULL)
1834 {
1835 m = &pseudomailer;
1836 m->m_mno = -1;
1837 m->m_name = "NULL";
1838 }
1839
abae7b2d
EA
1840 for (i = indent; i > 0; i--)
1841 printf("\t");
1842 printf("\tnext=%x, flags=%o, rmailer %d, alias=%x, sibling=%x, child=%x\n",
1843 a->q_next, a->q_flags, a->q_rmailer, a->q_alias,
1844 a->q_sibling, a->q_child);
1845
1846 /* follow the chain if appropriate */
779597d8
EA
1847 if (!follow)
1848 return;
abae7b2d
EA
1849
1850 indent++;
1851 printaddr(a->q_child, TRUE);
1852 indent--;
1853 a = a->q_sibling;
779597d8 1854 }
d4f42161 1855 if (first)
331b7c9f 1856 printf("[NULL]\n");
779597d8 1857}
74c5fe7c 1858
22892d62
EA
1859\f/*
1860** REMOTENAME -- return the name relative to the current mailer
1861**
1862** Parameters:
1863** name -- the name to translate.
b00e6882
EA
1864** m -- the mailer that we want to do rewriting relative
1865** to.
9e43a19e
EA
1866** flags -- fine tune operations.
1867** pstat -- pointer to status word.
68f7099c 1868** e -- the current envelope.
22892d62
EA
1869**
1870** Returns:
1871** the text string representing this address relative to
1872** the receiving mailer.
1873**
1874** Side Effects:
1875** none.
1876**
1877** Warnings:
1878** The text string returned is tucked away locally;
1879** copy it if you intend to save it.
1880*/
1881
1882char *
9e43a19e 1883remotename(name, m, flags, pstat, e)
22892d62 1884 char *name;
fd6cfc53 1885 MAILER *m;
9e43a19e
EA
1886 int flags;
1887 int *pstat;
a4076aed 1888 register ENVELOPE *e;
22892d62 1889{
b00e6882
EA
1890 register char **pvp;
1891 char *fancy;
a4076aed 1892 char *oldg = macvalue('g', e);
68f7099c 1893 int rwset;
b00e6882
EA
1894 static char buf[MAXNAME];
1895 char lbuf[MAXNAME];
217a0102 1896 char pvpbuf[PSBUFSIZE];
22892d62 1897
e4b94f39
EA
1898 if (tTd(12, 1))
1899 printf("remotename(%s)\n", name);
e4b94f39 1900
4db45bf1 1901 /* don't do anything if we are tagging it as special */
c40bef63 1902 if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
4db45bf1
EA
1903 return (name);
1904
22892d62 1905 /*
857afefe
EA
1906 ** Do a heuristic crack of this name to extract any comment info.
1907 ** This will leave the name as a comment and a $g macro.
22892d62
EA
1908 */
1909
9e43a19e 1910 if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
2bee003d 1911 fancy = "\201g";
532c9874
EA
1912 else
1913 fancy = crackaddr(name);
0908f182 1914
857afefe
EA
1915 /*
1916 ** Turn the name into canonical form.
1917 ** Normally this will be RFC 822 style, i.e., "user@domain".
1918 ** If this only resolves to "user", and the "C" flag is
1919 ** specified in the sending mailer, then the sender's
1920 ** domain will be appended.
1921 */
1922
9e2cf26f 1923 pvp = prescan(name, '\0', pvpbuf, NULL);
0908f182 1924 if (pvp == NULL)
22892d62 1925 return (name);
9e43a19e
EA
1926 if (rewrite(pvp, 3, e) == EX_TEMPFAIL)
1927 *pstat = EX_TEMPFAIL;
1928 if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
857afefe
EA
1929 {
1930 /* append from domain to this address */
1931 register char **pxp = pvp;
1932
18c3cee9 1933 /* see if there is an "@domain" in the current name */
857afefe
EA
1934 while (*pxp != NULL && strcmp(*pxp, "@") != 0)
1935 pxp++;
1936 if (*pxp == NULL)
1937 {
18c3cee9 1938 /* no.... append the "@domain" from the sender */
a4076aed 1939 register char **qxq = e->e_fromdomain;
857afefe 1940
18c3cee9
EA
1941 while ((*pxp++ = *qxq++) != NULL)
1942 continue;
9e43a19e
EA
1943 if (rewrite(pvp, 3, e) == EX_TEMPFAIL)
1944 *pstat = EX_TEMPFAIL;
857afefe
EA
1945 }
1946 }
1947
1948 /*
b726abe3 1949 ** Do more specific rewriting.
fd6cfc53
EA
1950 ** Rewrite using ruleset 1 or 2 for envelope addresses and
1951 ** 5 or 6 for header addresses depending on whether this
1952 ** is a sender address or not.
857afefe
EA
1953 ** Then run it through any receiving-mailer-specific rulesets.
1954 */
1955
b00e6882 1956 else
9e4c75d5 1957 {
68f7099c 1958 if (rwset > 0)
9e4c75d5 1959 {
9e43a19e
EA
1960 if (rewrite(pvp, rwset, e) == EX_TEMPFAIL)
1961 *pstat = EX_TEMPFAIL;
9e4c75d5 1962 }
22892d62 1963
b726abe3
EA
1964 /*
1965 ** Do any final sanitation the address may require.
1966 ** This will normally be used to turn internal forms
1967 ** (e.g., user@host.LOCAL) into external form. This
1968 ** may be used as a default to the above rules.
1969 */
1970
9e43a19e
EA
1971 if (rewrite(pvp, 4, e) == EX_TEMPFAIL)
1972 *pstat = EX_TEMPFAIL;
b726abe3 1973
857afefe
EA
1974 /*
1975 ** Now restore the comment information we had at the beginning.
1976 */
1977
9d6aecfd 1978 cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
a4076aed
EA
1979 define('g', lbuf, e);
1980 expand(fancy, buf, &buf[sizeof buf - 1], e);
1981 define('g', oldg, e);
22892d62 1982
22892d62 1983 if (tTd(12, 1))
e4b94f39 1984 printf("remotename => `%s'\n", buf);
22892d62
EA
1985 return (buf);
1986}
d13779b1 1987\f/*
fd6cfc53
EA
1988** UURELATIVIZE -- Make an address !-relative to recipient/sender nodes
1989**
1990** Parameters:
1991** from -- the sending node (usually "$k" or "$w")
1992** to -- the receiving node (usually "$h")
1993** pvp -- address vector
1994**
1995** Returns:
1996** none.
1997**
1998** Side Effects:
1999** The pvp is rewritten to be relative the "to" node
2000** wrt the "from" node. In other words, if the pvp
2001** is headed by "to!" that part is stripped; otherwise
2002** "from!" is prepended. Exception: "to!user" addresses
2003** with no '!'s in the user part are sent as is.
2004**
2005** Bugs:
2006** The pvp may overflow, but we don't catch it.
2007*/
2008
2009static void
2010uurelativize(from, to, pvp)
2011 const char *from, *to;
2012 char **pvp;
2013{
2014 register char **pxp = pvp;
2015 char expfrom[MAXNAME], expto[MAXNAME];
2016
2017 expand(from, expfrom, &expfrom[sizeof expfrom - 1], CurEnv);
2018 expand(to, expto, &expto[sizeof expto - 1], CurEnv);
2019
2020 /*
2021 * supposing that we've got something, should
2022 * we add "from!" or remove "to!"?
2023 */
2024 if (pvp[0] != NULL)
2025 if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 ||
2026 /*strcasecmp?*/ strcmp(pvp[0], expto) != 0)
2027 {
2028 /* either local name, no UUCP address, */
2029 /* or not to "to!" ==> prepend address with "from!" */
2030
2031 /* already there? */
2032 if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 ||
2033 /*strcasecmp?*/ strcmp(pvp[0], expfrom) != 0)
2034 {
2035
2036 /* no, put it there */
2037 while (*pxp != NULL)
2038 pxp++;
2039 do
2040 pxp[2] = *pxp;
2041 while (pxp-- != pvp);
2042 pvp[0] = newstr(expfrom);
2043 pvp[1] = "!";
2044 }
2045 }
2046 else
2047 {
2048 /* address is to "to!" -- remove if not "to!user" */
2049 for (pxp = &pvp[2];
2050 *pxp != NULL && strcmp(*pxp, "!") != 0; pxp++)
2051 ;
2052 if (*pxp != NULL)
2053 for (pxp = pvp; *pxp != NULL; pxp++)
2054 *pxp = pxp[2];
2055 }
2056}
2057\f/*
d13779b1
EA
2058** MAPLOCALUSER -- run local username through ruleset 5 for final redirection
2059**
2060** Parameters:
2061** a -- the address to map (but just the user name part).
2062** sendq -- the sendq in which to install any replacement
2063** addresses.
2064**
2065** Returns:
2066** none.
2067*/
2068
a4076aed 2069maplocaluser(a, sendq, e)
d13779b1
EA
2070 register ADDRESS *a;
2071 ADDRESS **sendq;
a4076aed 2072 ENVELOPE *e;
d13779b1
EA
2073{
2074 register char **pvp;
2075 register ADDRESS *a1 = NULL;
9e2cf26f 2076 auto char *delimptr;
d13779b1
EA
2077 char pvpbuf[PSBUFSIZE];
2078
2079 if (tTd(29, 1))
2080 {
2081 printf("maplocaluser: ");
2082 printaddr(a, FALSE);
2083 }
9e2cf26f 2084 pvp = prescan(a->q_user, '\0', pvpbuf, &delimptr);
d13779b1
EA
2085 if (pvp == NULL)
2086 return;
2087
d1db7a89 2088 (void) rewrite(pvp, 5, e);
2bee003d 2089 if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
d13779b1
EA
2090 return;
2091
2092 /* if non-null, mailer destination specified -- has it changed? */
28f94061 2093 a1 = buildaddr(pvp, NULL, 0, e);
d13779b1
EA
2094 if (a1 == NULL || sameaddr(a, a1))
2095 return;
2096
2097 /* mark old address as dead; insert new address */
2098 a->q_flags |= QDONTSEND;
78bbbc48
EA
2099 if (tTd(29, 5))
2100 {
2101 printf("maplocaluser: QDONTSEND ");
2102 printaddr(a, FALSE);
2103 }
d13779b1 2104 a1->q_alias = a;
dab5fedb 2105 allocaddr(a1, RF_COPYALL, NULL);
a4076aed 2106 (void) recipient(a1, sendq, e);
d13779b1 2107}
28d46d78
EA
2108\f/*
2109** DEQUOTE_INIT -- initialize dequote map
2110**
2111** This is a no-op.
2112**
2113** Parameters:
2114** map -- the internal map structure.
28d46d78
EA
2115** args -- arguments.
2116**
2117** Returns:
2118** TRUE.
2119*/
2120
2121bool
9114f86c 2122dequote_init(map, args)
28d46d78 2123 MAP *map;
28d46d78
EA
2124 char *args;
2125{
5111538e
EA
2126 register char *p = args;
2127
2128 for (;;)
2129 {
2130 while (isascii(*p) && isspace(*p))
2131 p++;
2132 if (*p != '-')
2133 break;
2134 switch (*++p)
2135 {
2136 case 'a':
2137 map->map_app = ++p;
2138 break;
2139 }
2140 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2141 p++;
2142 if (*p != '\0')
2143 *p = '\0';
2144 }
2145 if (map->map_app != NULL)
2146 map->map_app = newstr(map->map_app);
2147
28d46d78
EA
2148 return TRUE;
2149}
2150\f/*
2151** DEQUOTE_MAP -- unquote an address
2152**
2153** Parameters:
2154** map -- the internal map structure (ignored).
713c523f 2155** name -- the name to dequote.
28d46d78 2156** av -- arguments (ignored).
d1db7a89 2157** statp -- pointer to status out-parameter.
28d46d78
EA
2158**
2159** Returns:
2160** NULL -- if there were no quotes, or if the resulting
2161** unquoted buffer would not be acceptable to prescan.
2162** else -- The dequoted buffer.
2163*/
2164
2165char *
713c523f 2166dequote_map(map, name, av, statp)
28d46d78 2167 MAP *map;
713c523f 2168 char *name;
28d46d78 2169 char **av;
d1db7a89 2170 int *statp;
28d46d78
EA
2171{
2172 register char *p;
2173 register char *q;
2174 register char c;
2175 int anglecnt;
2176 int cmntcnt;
2177 int quotecnt;
493ddf6a 2178 int spacecnt;
28d46d78
EA
2179 bool quotemode;
2180 bool bslashmode;
2181
2182 anglecnt = 0;
2183 cmntcnt = 0;
2184 quotecnt = 0;
493ddf6a 2185 spacecnt = 0;
28d46d78
EA
2186 quotemode = FALSE;
2187 bslashmode = FALSE;
2188
713c523f 2189 for (p = q = name; (c = *p++) != '\0'; )
28d46d78
EA
2190 {
2191 if (bslashmode)
2192 {
2193 bslashmode = FALSE;
2194 *q++ = c;
2195 continue;
2196 }
2197
2198 switch (c)
2199 {
2200 case '\\':
2201 bslashmode = TRUE;
2202 break;
2203
2204 case '(':
2205 cmntcnt++;
2206 break;
2207
2208 case ')':
2209 if (cmntcnt-- <= 0)
2210 return NULL;
2211 break;
493ddf6a
EA
2212
2213 case ' ':
2214 spacecnt++;
2215 break;
28d46d78
EA
2216 }
2217
2218 if (cmntcnt > 0)
2219 {
2220 *q++ = c;
2221 continue;
2222 }
2223
2224 switch (c)
2225 {
2226 case '"':
2227 quotemode = !quotemode;
2228 quotecnt++;
2229 continue;
2230
2231 case '<':
2232 anglecnt++;
2233 break;
2234
2235 case '>':
2236 if (anglecnt-- <= 0)
2237 return NULL;
2238 break;
2239 }
2240 *q++ = c;
2241 }
2242
2243 if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
493ddf6a 2244 quotemode || quotecnt <= 0 || spacecnt != 0)
28d46d78
EA
2245 return NULL;
2246 *q++ = '\0';
713c523f 2247 return name;
28d46d78 2248}