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