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