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