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