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