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