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