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