install correct aliases file
[unix-history] / usr / src / usr.sbin / sendmail / src / parseaddr.c
CommitLineData
f0e070dc 1/*
dc45ba8c 2 * Copyright (c) 1983 Eric P. Allman
bee79b64
KB
3 * Copyright (c) 1988 Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms are permitted
dc45ba8c
KB
7 * provided that the above copyright notice and this paragraph are
8 * duplicated in all such forms and that any documentation,
9 * advertising materials, and other materials related to such
10 * distribution and use acknowledge that the software was developed
11 * by the University of California, Berkeley. The name of the
12 * University may not be used to endorse or promote products derived
13 * from this software without specific prior written permission.
14 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
bee79b64 17 */
f0e070dc
MAN
18
19#ifndef lint
dc45ba8c 20static char sccsid[] = "@(#)parseaddr.c 5.9 (Berkeley) %G%";
bee79b64 21#endif /* not lint */
f0e070dc
MAN
22
23# include "sendmail.h"
916b3375 24
b3cbe40f 25/*
40d27fed 26** PARSEADDR -- Parse an address
b3cbe40f
EA
27**
28** Parses an address and breaks it up into three parts: a
29** net to transmit the message on, the host to transmit it
30** to, and a user on that host. These are loaded into an
406f98bc 31** ADDRESS header with the values squirreled away if necessary.
b3cbe40f
EA
32** The "user" part may not be a real user; the process may
33** just reoccur on that machine. For example, on a machine
34** with an arpanet connection, the address
35** csvax.bill@berkeley
36** will break up to a "user" of 'csvax.bill' and a host
37** of 'berkeley' -- to be transmitted over the arpanet.
38**
39** Parameters:
40** addr -- the address to parse.
41** a -- a pointer to the address descriptor buffer.
42** If NULL, a header will be created.
43** copyf -- determines what shall be copied:
44** -1 -- don't copy anything. The printname
45** (q_paddr) is just addr, and the
46** user & host are allocated internally
47** to parse.
48** 0 -- copy out the parsed user & host, but
49** don't copy the printname.
50** +1 -- copy everything.
d3f52e20
EA
51** delim -- the character to terminate the address, passed
52** to prescan.
b3cbe40f
EA
53**
54** Returns:
55** A pointer to the address descriptor header (`a' if
56** `a' is non-NULL).
57** NULL on error.
58**
59** Side Effects:
60** none
b3cbe40f
EA
61*/
62
7338e3d4 63/* following delimiters are inherent to the internal algorithms */
a73ae8ac 64# define DELIMCHARS "\001()<>,;\\\"\r\n" /* word delimiters */
ecf90b7d 65
406f98bc 66ADDRESS *
d3f52e20 67parseaddr(addr, a, copyf, delim)
b3cbe40f 68 char *addr;
406f98bc 69 register ADDRESS *a;
b3cbe40f 70 int copyf;
d3f52e20 71 char delim;
b3cbe40f 72{
d6a28dd8
EA
73 register char **pvp;
74 register struct mailer *m;
217a0102 75 char pvpbuf[PSBUFSIZE];
d6a28dd8 76 extern char **prescan();
d6a28dd8 77 extern ADDRESS *buildaddr();
b3cbe40f
EA
78
79 /*
80 ** Initialize and prescan address.
81 */
82
2654b031 83 CurEnv->e_to = addr;
9e3c0a28 84# ifdef DEBUG
9678c96d 85 if (tTd(20, 1))
40d27fed 86 printf("\n--parseaddr(%s)\n", addr);
9e3c0a28
EA
87# endif DEBUG
88
217a0102 89 pvp = prescan(addr, delim, pvpbuf);
d6a28dd8 90 if (pvp == NULL)
b3cbe40f
EA
91 return (NULL);
92
93 /*
d6a28dd8 94 ** Apply rewriting rules.
0908f182 95 ** Ruleset 0 does basic parsing. It must resolve.
b3cbe40f
EA
96 */
97
857afefe 98 rewrite(pvp, 3);
f65e7ded 99 rewrite(pvp, 0);
b3cbe40f 100
d6a28dd8
EA
101 /*
102 ** See if we resolved to a real mailer.
103 */
b3cbe40f 104
d6a28dd8
EA
105 if (pvp[0][0] != CANONNET)
106 {
107 setstat(EX_USAGE);
108 usrerr("cannot resolve name");
109 return (NULL);
b3cbe40f
EA
110 }
111
112 /*
d6a28dd8 113 ** Build canonical address from pvp.
b3cbe40f
EA
114 */
115
d6a28dd8 116 a = buildaddr(pvp, a);
fe43b434
EA
117 if (a == NULL)
118 return (NULL);
179c1218 119 m = a->q_mailer;
b3cbe40f
EA
120
121 /*
d6a28dd8
EA
122 ** Make local copies of the host & user and then
123 ** transport them out.
b3cbe40f
EA
124 */
125
b3cbe40f 126 if (copyf > 0)
506fc377
EA
127 {
128 extern char *DelimChar;
129 char savec = *DelimChar;
130
131 *DelimChar = '\0';
406f98bc 132 a->q_paddr = newstr(addr);
506fc377
EA
133 *DelimChar = savec;
134 }
b3cbe40f
EA
135 else
136 a->q_paddr = addr;
2e3062fe
EA
137
138 if (a->q_user == NULL)
139 a->q_user = "";
140 if (a->q_host == NULL)
141 a->q_host = "";
142
d6a28dd8 143 if (copyf >= 0)
b3cbe40f 144 {
2e3062fe 145 a->q_host = newstr(a->q_host);
d6a28dd8
EA
146 if (a->q_user != a->q_paddr)
147 a->q_user = newstr(a->q_user);
b3cbe40f
EA
148 }
149
1ae66bfb
EA
150 /*
151 ** Convert host name to lower case if requested.
152 ** User name will be done later.
153 */
154
155 if (!bitnset(M_HST_UPPER, m->m_flags))
156 makelower(a->q_host);
157
b3cbe40f
EA
158 /*
159 ** Compute return value.
160 */
161
162# ifdef DEBUG
9678c96d 163 if (tTd(20, 1))
331b7c9f 164 {
40d27fed 165 printf("parseaddr-->");
331b7c9f
EA
166 printaddr(a, FALSE);
167 }
b3cbe40f
EA
168# endif DEBUG
169
170 return (a);
171}
172\f/*
0fe3917f
EA
173** LOWERADDR -- map UPPER->lower case on addresses as requested.
174**
175** Parameters:
176** a -- address to be mapped.
177**
178** Returns:
179** none.
180**
181** Side Effects:
182** none.
183*/
184
185loweraddr(a)
186 register ADDRESS *a;
187{
188 register MAILER *m = a->q_mailer;
189
0fe3917f
EA
190 if (!bitnset(M_USR_UPPER, m->m_flags))
191 makelower(a->q_user);
192}
193\f/*
b3cbe40f
EA
194** PRESCAN -- Prescan name and make it canonical
195**
7338e3d4
EA
196** Scans a name and turns it into a set of tokens. This process
197** deletes blanks and comments (in parentheses).
b3cbe40f
EA
198**
199** This routine knows about quoted strings and angle brackets.
200**
201** There are certain subtleties to this routine. The one that
202** comes to mind now is that backslashes on the ends of names
203** are silently stripped off; this is intentional. The problem
204** is that some versions of sndmsg (like at LBL) set the kill
205** character to something other than @ when reading addresses;
206** so people type "csvax.eric\@berkeley" -- which screws up the
207** berknet mailer.
208**
209** Parameters:
210** addr -- the name to chomp.
b3cbe40f
EA
211** delim -- the delimiter for the address, normally
212** '\0' or ','; \0 is accepted in any case.
f12f79be 213** If '\t' then we are reading the .cf file.
217a0102
EA
214** pvpbuf -- place to put the saved text -- note that
215** the pointers are static.
b3cbe40f
EA
216**
217** Returns:
d6a28dd8 218** A pointer to a vector of tokens.
b3cbe40f
EA
219** NULL on error.
220**
221** Side Effects:
3312f93c 222** sets DelimChar to point to the character matching 'delim'.
b3cbe40f
EA
223*/
224
506fc377
EA
225/* states and character types */
226# define OPR 0 /* operator */
227# define ATM 1 /* atom */
228# define QST 2 /* in quoted string */
229# define SPC 3 /* chewing up spaces */
230# define ONE 4 /* pick up one character */
231
232# define NSTATES 5 /* number of states */
233# define TYPE 017 /* mask to select state type */
234
235/* meta bits for table */
236# define M 020 /* meta character; don't pass through */
237# define B 040 /* cause a break */
238# define MB M|B /* meta-break */
239
240static short StateTab[NSTATES][NSTATES] =
241{
921e1125 242 /* oldst chtype> OPR ATM QST SPC ONE */
1f513630
EA
243 /*OPR*/ OPR|B, ATM|B, QST|B, SPC|MB, ONE|B,
244 /*ATM*/ OPR|B, ATM, QST|B, SPC|MB, ONE|B,
245 /*QST*/ QST, QST, OPR, QST, QST,
506fc377
EA
246 /*SPC*/ OPR, ATM, QST, SPC|M, ONE,
247 /*ONE*/ OPR, OPR, OPR, OPR, OPR,
248};
249
250# define NOCHAR -1 /* signal nothing in lookahead token */
251
252char *DelimChar; /* set to point to the delimiter */
d6a28dd8
EA
253
254char **
217a0102 255prescan(addr, delim, pvpbuf)
b3cbe40f 256 char *addr;
b3cbe40f 257 char delim;
217a0102 258 char pvpbuf[];
b3cbe40f
EA
259{
260 register char *p;
506fc377 261 register char *q;
611b763d 262 register int c;
d6a28dd8 263 char **avp;
b3cbe40f
EA
264 bool bslashmode;
265 int cmntcnt;
e93460a9 266 int anglecnt;
d6a28dd8 267 char *tok;
506fc377
EA
268 int state;
269 int newstate;
506fc377 270 static char *av[MAXATOM+1];
15cd119d
EA
271 extern int errno;
272
273 /* make sure error messages don't have garbage on them */
274 errno = 0;
b3cbe40f 275
217a0102 276 q = pvpbuf;
d6a28dd8 277 bslashmode = FALSE;
43ff8bb5 278 cmntcnt = 0;
e93460a9 279 anglecnt = 0;
d6a28dd8 280 avp = av;
506fc377
EA
281 state = OPR;
282 c = NOCHAR;
283 p = addr;
284# ifdef DEBUG
285 if (tTd(22, 45))
286 {
287 printf("prescan: ");
288 xputs(p);
03388044 289 (void) putchar('\n');
506fc377
EA
290 }
291# endif DEBUG
292
293 do
b3cbe40f 294 {
d6a28dd8
EA
295 /* read a token */
296 tok = q;
506fc377 297 for (;;)
b3cbe40f 298 {
506fc377
EA
299 /* store away any old lookahead character */
300 if (c != NOCHAR)
301 {
f12f79be 302 /* see if there is room */
217a0102 303 if (q >= &pvpbuf[PSBUFSIZE - 5])
506fc377
EA
304 {
305 usrerr("Address too long");
306 DelimChar = p;
307 return (NULL);
308 }
f12f79be
EA
309
310 /* squirrel it away */
506fc377
EA
311 *q++ = c;
312 }
313
314 /* read a new input character */
315 c = *p++;
316 if (c == '\0')
317 break;
f12f79be
EA
318 c &= ~0200;
319
506fc377
EA
320# ifdef DEBUG
321 if (tTd(22, 101))
322 printf("c=%c, s=%d; ", c, state);
323# endif DEBUG
324
d6a28dd8
EA
325 /* chew up special characters */
326 *q = '\0';
327 if (bslashmode)
328 {
2e3062fe
EA
329 /* kludge \! for naive users */
330 if (c != '!')
331 c |= 0200;
d6a28dd8
EA
332 bslashmode = FALSE;
333 }
334 else if (c == '\\')
335 {
336 bslashmode = TRUE;
506fc377 337 c = NOCHAR;
d6a28dd8 338 }
63a35680
EA
339 else if (state == QST)
340 {
341 /* do nothing, just avoid next clauses */
342 }
506fc377 343 else if (c == '(')
cdb17311 344 {
506fc377
EA
345 cmntcnt++;
346 c = NOCHAR;
cdb17311 347 }
506fc377 348 else if (c == ')')
d6a28dd8 349 {
506fc377 350 if (cmntcnt <= 0)
d6a28dd8 351 {
506fc377
EA
352 usrerr("Unbalanced ')'");
353 DelimChar = p;
354 return (NULL);
d6a28dd8 355 }
506fc377
EA
356 else
357 cmntcnt--;
d6a28dd8 358 }
506fc377
EA
359 else if (cmntcnt > 0)
360 c = NOCHAR;
e93460a9
EA
361 else if (c == '<')
362 anglecnt++;
363 else if (c == '>')
364 {
365 if (anglecnt <= 0)
366 {
367 usrerr("Unbalanced '>'");
368 DelimChar = p;
369 return (NULL);
370 }
371 anglecnt--;
372 }
926671ee
EA
373 else if (delim == ' ' && isspace(c))
374 c = ' ';
7b955214
EA
375 else if (c == ':' && !CurEnv->e_oldstyle)
376 {
377 /* consume characters until a semicolon */
378 while (*p != '\0' && *p != ';')
379 p++;
380 if (*p == '\0')
381 usrerr("Unbalanced ':...;' group spec");
382 else
383 p++;
384 c = ' ';
385 }
d6a28dd8 386
506fc377
EA
387 if (c == NOCHAR)
388 continue;
d6a28dd8 389
506fc377 390 /* see if this is end of input */
f115fb3a 391 if (c == delim && anglecnt <= 0 && state != QST)
506fc377 392 break;
d6a28dd8 393
506fc377
EA
394 newstate = StateTab[state][toktype(c)];
395# ifdef DEBUG
396 if (tTd(22, 101))
397 printf("ns=%02o\n", newstate);
398# endif DEBUG
399 state = newstate & TYPE;
400 if (bitset(M, newstate))
401 c = NOCHAR;
402 if (bitset(B, newstate))
d6a28dd8 403 break;
b3cbe40f 404 }
d6a28dd8
EA
405
406 /* new token */
506fc377 407 if (tok != q)
2a119b55 408 {
506fc377
EA
409 *q++ = '\0';
410# ifdef DEBUG
411 if (tTd(22, 36))
b3cbe40f 412 {
506fc377
EA
413 printf("tok=");
414 xputs(tok);
03388044 415 (void) putchar('\n');
b3cbe40f 416 }
506fc377
EA
417# endif DEBUG
418 if (avp >= &av[MAXATOM])
b3cbe40f 419 {
506fc377
EA
420 syserr("prescan: too many tokens");
421 DelimChar = p;
422 return (NULL);
b3cbe40f 423 }
506fc377 424 *avp++ = tok;
b3cbe40f 425 }
e93460a9 426 } while (c != '\0' && (c != delim || anglecnt > 0));
d6a28dd8 427 *avp = NULL;
506fc377 428 DelimChar = --p;
d6a28dd8
EA
429 if (cmntcnt > 0)
430 usrerr("Unbalanced '('");
e93460a9
EA
431 else if (anglecnt > 0)
432 usrerr("Unbalanced '<'");
506fc377 433 else if (state == QST)
d6a28dd8
EA
434 usrerr("Unbalanced '\"'");
435 else if (av[0] != NULL)
436 return (av);
437 return (NULL);
438}
439\f/*
440** TOKTYPE -- return token type
441**
442** Parameters:
443** c -- the character in question.
444**
445** Returns:
446** Its type.
447**
448** Side Effects:
449** none.
450*/
b3cbe40f 451
d6a28dd8
EA
452toktype(c)
453 register char c;
454{
05f1e115 455 static char buf[50];
e232ff47 456 static bool firstime = TRUE;
05f1e115 457
e232ff47 458 if (firstime)
05f1e115 459 {
e232ff47 460 firstime = FALSE;
a73ae8ac 461 expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
f9566d23 462 (void) strcat(buf, DELIMCHARS);
05f1e115 463 }
9f39d7cd 464 if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
506fc377
EA
465 return (ONE);
466 if (c == '"')
467 return (QST);
cdb17311 468 if (!isascii(c))
506fc377
EA
469 return (ATM);
470 if (isspace(c) || c == ')')
471 return (SPC);
05f1e115 472 if (iscntrl(c) || index(buf, c) != NULL)
506fc377
EA
473 return (OPR);
474 return (ATM);
d6a28dd8
EA
475}
476\f/*
477** REWRITE -- apply rewrite rules to token vector.
478**
2258fda6
EA
479** This routine is an ordered production system. Each rewrite
480** rule has a LHS (called the pattern) and a RHS (called the
481** rewrite); 'rwr' points the the current rewrite rule.
482**
483** For each rewrite rule, 'avp' points the address vector we
484** are trying to match against, and 'pvp' points to the pattern.
792a6b53 485** If pvp points to a special match value (MATCHZANY, MATCHANY,
9f39d7cd
EA
486** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
487** matched is saved away in the match vector (pointed to by 'mvp').
2258fda6
EA
488**
489** When a match between avp & pvp does not match, we try to
9f39d7cd 490** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
2258fda6 491** we must also back out the match in mvp. If we reach a
792a6b53
EA
492** MATCHANY or MATCHZANY we just extend the match and start
493** over again.
2258fda6
EA
494**
495** When we finally match, we rewrite the address vector
496** and try over again.
497**
d6a28dd8
EA
498** Parameters:
499** pvp -- pointer to token vector.
500**
501** Returns:
502** none.
503**
504** Side Effects:
505** pvp is modified.
506*/
507
508struct match
509{
54e8e18d
EA
510 char **first; /* first token matched */
511 char **last; /* last token matched */
d6a28dd8
EA
512};
513
54e8e18d 514# define MAXMATCH 9 /* max params per rewrite */
d6a28dd8
EA
515
516
f65e7ded 517rewrite(pvp, ruleset)
d6a28dd8 518 char **pvp;
f65e7ded 519 int ruleset;
d6a28dd8
EA
520{
521 register char *ap; /* address pointer */
522 register char *rp; /* rewrite pointer */
523 register char **avp; /* address vector pointer */
524 register char **rvp; /* rewrite vector pointer */
792a6b53
EA
525 register struct match *mlp; /* cur ptr into mlist */
526 register struct rewrite *rwr; /* pointer to current rewrite rule */
41173b8f
EA
527 int subr; /* subroutine number if >= 0 */
528 bool dolookup; /* do host aliasing */
54e8e18d 529 struct match mlist[MAXMATCH]; /* stores match on LHS */
d6a28dd8
EA
530 char *npvp[MAXATOM+1]; /* temporary space for rebuild */
531
75f95954 532 if (OpMode == MD_TEST || tTd(21, 2))
d6a28dd8 533 {
b726abe3 534 printf("rewrite: ruleset %2d input:", ruleset);
d6a28dd8
EA
535 printav(pvp);
536 }
e93460a9
EA
537 if (pvp == NULL)
538 return;
d6a28dd8
EA
539
540 /*
541 ** Run through the list of rewrite rules, applying
542 ** any that match.
543 */
544
f65e7ded 545 for (rwr = RewriteRules[ruleset]; rwr != NULL; )
d6a28dd8 546 {
cdb17311 547# ifdef DEBUG
9678c96d 548 if (tTd(21, 12))
d6a28dd8 549 {
b00e6882 550 printf("-----trying rule:");
d6a28dd8
EA
551 printav(rwr->r_lhs);
552 }
cdb17311 553# endif DEBUG
ecf90b7d 554
d6a28dd8 555 /* try to match on this rule */
54e8e18d 556 mlp = mlist;
792a6b53
EA
557 rvp = rwr->r_lhs;
558 avp = pvp;
559 while ((ap = *avp) != NULL || *rvp != NULL)
b3cbe40f 560 {
d6a28dd8 561 rp = *rvp;
792a6b53
EA
562# ifdef DEBUG
563 if (tTd(21, 35))
564 {
b00e6882 565 printf("ap=");
792a6b53 566 xputs(ap);
b00e6882 567 printf(", rp=");
792a6b53 568 xputs(rp);
b00e6882 569 printf("\n");
792a6b53
EA
570 }
571# endif DEBUG
d6a28dd8 572 if (rp == NULL)
b3cbe40f 573 {
d6a28dd8 574 /* end-of-pattern before end-of-address */
792a6b53
EA
575 goto backup;
576 }
577 if (ap == NULL && *rp != MATCHZANY)
578 {
579 /* end-of-input */
580 break;
d6a28dd8
EA
581 }
582
583 switch (*rp)
584 {
13834bfc 585 register STAB *s;
13834bfc 586
13834bfc 587 case MATCHCLASS:
9f39d7cd
EA
588 case MATCHNCLASS:
589 /* match any token in (not in) a class */
cdb17311 590 s = stab(ap, ST_CLASS, ST_FIND);
1dbda134 591 if (s == NULL || !bitnset(rp[1], s->s_class))
9f39d7cd
EA
592 {
593 if (*rp == MATCHCLASS)
594 goto backup;
595 }
596 else if (*rp == MATCHNCLASS)
792a6b53 597 goto backup;
54e8e18d 598
2258fda6
EA
599 /* explicit fall-through */
600
601 case MATCHONE:
602 case MATCHANY:
603 /* match exactly one token */
792a6b53
EA
604 mlp->first = avp;
605 mlp->last = avp++;
606 mlp++;
607 break;
608
609 case MATCHZANY:
610 /* match zero or more tokens */
611 mlp->first = avp;
612 mlp->last = avp - 1;
54e8e18d 613 mlp++;
13834bfc
EA
614 break;
615
d6a28dd8
EA
616 default:
617 /* must have exact match */
ed73ef1d 618 if (strcasecmp(rp, ap))
792a6b53 619 goto backup;
54e8e18d 620 avp++;
d6a28dd8
EA
621 break;
622 }
623
624 /* successful match on this token */
d6a28dd8
EA
625 rvp++;
626 continue;
627
792a6b53 628 backup:
d6a28dd8
EA
629 /* match failed -- back up */
630 while (--rvp >= rwr->r_lhs)
631 {
632 rp = *rvp;
792a6b53 633 if (*rp == MATCHANY || *rp == MATCHZANY)
54e8e18d 634 {
2258fda6 635 /* extend binding and continue */
792a6b53
EA
636 avp = ++mlp[-1].last;
637 avp++;
2258fda6 638 rvp++;
d6a28dd8 639 break;
54e8e18d 640 }
2258fda6 641 avp--;
9f39d7cd
EA
642 if (*rp == MATCHONE || *rp == MATCHCLASS ||
643 *rp == MATCHNCLASS)
d6a28dd8 644 {
54e8e18d 645 /* back out binding */
54e8e18d 646 mlp--;
d6a28dd8
EA
647 }
648 }
649
650 if (rvp < rwr->r_lhs)
651 {
652 /* total failure to match */
653 break;
b3cbe40f 654 }
b3cbe40f 655 }
d6a28dd8
EA
656
657 /*
658 ** See if we successfully matched
659 */
660
7338e3d4 661 if (rvp < rwr->r_lhs || *rvp != NULL)
d6a28dd8 662 {
cdb17311 663# ifdef DEBUG
7338e3d4
EA
664 if (tTd(21, 10))
665 printf("----- rule fails\n");
cdb17311 666# endif DEBUG
7338e3d4
EA
667 rwr = rwr->r_next;
668 continue;
669 }
d6a28dd8 670
7338e3d4
EA
671 rvp = rwr->r_rhs;
672# ifdef DEBUG
673 if (tTd(21, 12))
674 {
675 printf("-----rule matches:");
676 printav(rvp);
677 }
678# endif DEBUG
679
680 rp = *rvp;
681 if (*rp == CANONUSER)
682 {
683 rvp++;
684 rwr = rwr->r_next;
685 }
686 else if (*rp == CANONHOST)
687 {
688 rvp++;
689 rwr = NULL;
690 }
691 else if (*rp == CANONNET)
692 rwr = NULL;
693
694 /* substitute */
41173b8f 695 dolookup = FALSE;
7338e3d4
EA
696 for (avp = npvp; *rvp != NULL; rvp++)
697 {
698 register struct match *m;
699 register char **pp;
792a6b53 700
7338e3d4 701 rp = *rvp;
41173b8f
EA
702
703 /* check to see if we should do a lookup */
704 if (*rp == MATCHLOOKUP)
705 dolookup = TRUE;
706
707 /* see if there is substitution here */
217a0102 708 if (*rp == MATCHREPL)
d6a28dd8 709 {
217a0102
EA
710 /* substitute from LHS */
711 m = &mlist[rp[1] - '1'];
712 if (m >= mlp)
d6a28dd8 713 {
41173b8f 714 toolong:
217a0102 715 syserr("rewrite: ruleset %d: replacement out of bounds", ruleset);
7338e3d4
EA
716 return;
717 }
2258fda6 718# ifdef DEBUG
217a0102
EA
719 if (tTd(21, 15))
720 {
721 printf("$%c:", rp[1]);
722 pp = m->first;
723 while (pp <= m->last)
724 {
725 printf(" %x=\"", *pp);
726 (void) fflush(stdout);
727 printf("%s\"", *pp++);
728 }
729 printf("\n");
730 }
731# endif DEBUG
7338e3d4
EA
732 pp = m->first;
733 while (pp <= m->last)
e96bade8 734 {
217a0102
EA
735 if (avp >= &npvp[MAXATOM])
736 {
737 syserr("rewrite: expansion too long");
738 return;
739 }
740 *avp++ = *pp++;
e96bade8 741 }
d6a28dd8 742 }
217a0102 743 else
45ecb1bf 744 {
217a0102 745 /* vanilla replacement */
7338e3d4 746 if (avp >= &npvp[MAXATOM])
41173b8f 747 goto toolong;
217a0102 748 *avp++ = rp;
45ecb1bf 749 }
7338e3d4
EA
750 }
751 *avp++ = NULL;
41173b8f 752
217a0102
EA
753 /*
754 ** Check for any hostname lookups.
755 */
756
757 for (rvp = npvp; *rvp != NULL; rvp++)
758 {
759 char **hbrvp;
760 char **xpvp;
761 int trsize;
560a80d9 762 char *olddelimchar;
c0ef545b 763 char buf[MAXNAME + 1];
217a0102 764 char *pvpb1[MAXATOM + 1];
7ac75266 765 char pvpbuf[PSBUFSIZE];
560a80d9 766 extern char *DelimChar;
217a0102
EA
767
768 if (**rvp != HOSTBEGIN)
769 continue;
770
771 /*
772 ** Got a hostname lookup.
773 **
774 ** This could be optimized fairly easily.
775 */
776
777 hbrvp = rvp;
778
779 /* extract the match part */
780 while (*++rvp != NULL && **rvp != HOSTEND)
781 continue;
782 if (*rvp != NULL)
783 *rvp++ = NULL;
784
785 /* save the remainder of the input string */
786 trsize = (int) (avp - rvp + 1) * sizeof *rvp;
787 bcopy((char *) rvp, (char *) pvpb1, trsize);
788
789 /* look it up */
790 cataddr(++hbrvp, buf, sizeof buf);
791 maphostname(buf, sizeof buf);
792
793 /* scan the new host name */
560a80d9 794 olddelimchar = DelimChar;
217a0102 795 xpvp = prescan(buf, '\0', pvpbuf);
560a80d9 796 DelimChar = olddelimchar;
217a0102
EA
797 if (xpvp == NULL)
798 {
799 syserr("rewrite: cannot prescan canonical hostname: %s", buf);
f0e070dc 800 return;
217a0102
EA
801 }
802
803 /* append it to the token list */
7ac75266
EA
804 for (avp = --hbrvp; *xpvp != NULL; xpvp++)
805 {
806 *avp++ = newstr(*xpvp);
c0ef545b 807 if (avp >= &npvp[MAXATOM])
217a0102 808 goto toolong;
7ac75266 809 }
217a0102
EA
810
811 /* restore the old trailing information */
7c560c3f 812 for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
c0ef545b 813 if (avp >= &npvp[MAXATOM])
217a0102 814 goto toolong;
7ac75266
EA
815
816 break;
217a0102
EA
817 }
818
819 /*
820 ** Check for subroutine calls.
821 */
822
823
41173b8f
EA
824 /*
825 ** Do hostname lookup if requested.
826 */
827
828 if (dolookup)
7338e3d4 829 {
41173b8f
EA
830 extern char **maphost();
831
832 rvp = maphost(npvp);
d6a28dd8
EA
833 }
834 else
41173b8f
EA
835 rvp = npvp;
836
837 /*
838 ** See if this is a subroutine call.
839 */
840
841 if (**rvp == CALLSUBR)
d6a28dd8 842 {
41173b8f
EA
843 subr = atoi(*++rvp);
844 rvp++;
7338e3d4 845 }
41173b8f
EA
846 else
847 subr = -1;
848
849 /*
850 ** Copy result back to original string.
851 */
852
853 for (avp = pvp; *rvp != NULL; rvp++)
854 *avp++ = *rvp;
855 *avp = NULL;
856
857 /*
858 ** If this specified a subroutine, call it.
859 */
860
861 if (subr >= 0)
862 {
863# ifdef DEBUG
864 if (tTd(21, 3))
865 printf("-----callsubr %s\n", subr);
866# endif DEBUG
867 rewrite(pvp, subr);
868 }
869
870 /*
871 ** Done with rewriting this pass.
872 */
873
cdb17311 874# ifdef DEBUG
7338e3d4
EA
875 if (tTd(21, 4))
876 {
877 printf("rewritten as:");
878 printav(pvp);
d6a28dd8 879 }
7338e3d4 880# endif DEBUG
b3cbe40f 881 }
b00e6882 882
75f95954 883 if (OpMode == MD_TEST || tTd(21, 2))
b00e6882 884 {
b726abe3 885 printf("rewrite: ruleset %2d returns:", ruleset);
b00e6882
EA
886 printav(pvp);
887 }
d6a28dd8
EA
888}
889\f/*
d6a28dd8
EA
890** BUILDADDR -- build address from token vector.
891**
892** Parameters:
893** tv -- token vector.
894** a -- pointer to address descriptor to fill.
895** If NULL, one will be allocated.
896**
897** Returns:
fe43b434
EA
898** NULL if there was an error.
899** 'a' otherwise.
d6a28dd8
EA
900**
901** Side Effects:
902** fills in 'a'
903*/
904
905ADDRESS *
906buildaddr(tv, a)
907 register char **tv;
908 register ADDRESS *a;
909{
d6a28dd8
EA
910 static char buf[MAXNAME];
911 struct mailer **mp;
912 register struct mailer *m;
d6a28dd8
EA
913
914 if (a == NULL)
915 a = (ADDRESS *) xalloc(sizeof *a);
abae7b2d 916 clear((char *) a, sizeof *a);
d6a28dd8
EA
917
918 /* figure out what net/mailer to use */
919 if (**tv != CANONNET)
fe43b434 920 {
d6a28dd8 921 syserr("buildaddr: no net");
fe43b434
EA
922 return (NULL);
923 }
d6a28dd8 924 tv++;
ed73ef1d 925 if (!strcasecmp(*tv, "error"))
fe43b434 926 {
4fd73cf9
EA
927 if (**++tv == CANONHOST)
928 {
929 setstat(atoi(*++tv));
930 tv++;
931 }
932 if (**tv != CANONUSER)
fe43b434
EA
933 syserr("buildaddr: error: no user");
934 buf[0] = '\0';
935 while (*++tv != NULL)
936 {
937 if (buf[0] != '\0')
f9566d23
EA
938 (void) strcat(buf, " ");
939 (void) strcat(buf, *tv);
fe43b434
EA
940 }
941 usrerr(buf);
942 return (NULL);
943 }
179c1218 944 for (mp = Mailer; (m = *mp++) != NULL; )
d6a28dd8 945 {
ed73ef1d 946 if (!strcasecmp(m->m_name, *tv))
d6a28dd8
EA
947 break;
948 }
949 if (m == NULL)
fe43b434 950 {
2e3062fe 951 syserr("buildaddr: unknown mailer %s", *tv);
fe43b434
EA
952 return (NULL);
953 }
179c1218 954 a->q_mailer = m;
d6a28dd8
EA
955
956 /* figure out what host (if any) */
957 tv++;
1dbda134 958 if (!bitnset(M_LOCAL, m->m_flags))
d6a28dd8 959 {
35cc3fad 960 if (**tv++ != CANONHOST)
fe43b434 961 {
d6a28dd8 962 syserr("buildaddr: no host");
fe43b434
EA
963 return (NULL);
964 }
35cc3fad
EA
965 buf[0] = '\0';
966 while (*tv != NULL && **tv != CANONUSER)
f9566d23 967 (void) strcat(buf, *tv++);
35cc3fad 968 a->q_host = newstr(buf);
d6a28dd8
EA
969 }
970 else
971 a->q_host = NULL;
972
973 /* figure out the user */
974 if (**tv != CANONUSER)
fe43b434 975 {
d6a28dd8 976 syserr("buildaddr: no user");
fe43b434
EA
977 return (NULL);
978 }
5108b0cc
EA
979
980 /* rewrite according recipient mailer rewriting rules */
981 rewrite(++tv, 2);
982 if (m->m_r_rwset > 0)
983 rewrite(tv, m->m_r_rwset);
984 rewrite(tv, 4);
985
986 /* save the result for the command line/RCPT argument */
f5811c6c 987 cataddr(tv, buf, sizeof buf);
d6a28dd8
EA
988 a->q_user = buf;
989
990 return (a);
991}
9e3c0a28 992\f/*
31b33174
EA
993** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
994**
995** Parameters:
996** pvp -- parameter vector to rebuild.
997** buf -- buffer to build the string into.
998** sz -- size of buf.
999**
1000** Returns:
1001** none.
1002**
1003** Side Effects:
1004** Destroys buf.
1005*/
1006
1007cataddr(pvp, buf, sz)
1008 char **pvp;
1009 char *buf;
1010 register int sz;
1011{
1012 bool oatomtok = FALSE;
1013 bool natomtok = FALSE;
1014 register int i;
1015 register char *p;
1016
e93460a9
EA
1017 if (pvp == NULL)
1018 {
03388044 1019 (void) strcpy(buf, "");
e93460a9
EA
1020 return;
1021 }
31b33174 1022 p = buf;
89dd4c8f 1023 sz -= 2;
31b33174
EA
1024 while (*pvp != NULL && (i = strlen(*pvp)) < sz)
1025 {
506fc377 1026 natomtok = (toktype(**pvp) == ATM);
31b33174 1027 if (oatomtok && natomtok)
1744384a 1028 *p++ = SpaceSub;
31b33174
EA
1029 (void) strcpy(p, *pvp);
1030 oatomtok = natomtok;
1031 p += i;
89dd4c8f 1032 sz -= i + 1;
31b33174
EA
1033 pvp++;
1034 }
1035 *p = '\0';
1036}
1037\f/*
9e3c0a28
EA
1038** SAMEADDR -- Determine if two addresses are the same
1039**
1040** This is not just a straight comparison -- if the mailer doesn't
1041** care about the host we just ignore it, etc.
1042**
1043** Parameters:
1044** a, b -- pointers to the internal forms to compare.
9e3c0a28
EA
1045**
1046** Returns:
1047** TRUE -- they represent the same mailbox.
1048** FALSE -- they don't.
1049**
1050** Side Effects:
1051** none.
1052*/
1053
1054bool
7338e3d4 1055sameaddr(a, b)
9e3c0a28
EA
1056 register ADDRESS *a;
1057 register ADDRESS *b;
9e3c0a28
EA
1058{
1059 /* if they don't have the same mailer, forget it */
1060 if (a->q_mailer != b->q_mailer)
1061 return (FALSE);
1062
1063 /* if the user isn't the same, we can drop out */
7338e3d4 1064 if (strcmp(a->q_user, b->q_user) != 0)
9e3c0a28
EA
1065 return (FALSE);
1066
1067 /* if the mailer ignores hosts, we have succeeded! */
1dbda134 1068 if (bitnset(M_LOCAL, a->q_mailer->m_flags))
9e3c0a28
EA
1069 return (TRUE);
1070
1071 /* otherwise compare hosts (but be careful for NULL ptrs) */
1072 if (a->q_host == NULL || b->q_host == NULL)
1073 return (FALSE);
1074 if (strcmp(a->q_host, b->q_host) != 0)
1075 return (FALSE);
1076
1077 return (TRUE);
1078}
779597d8
EA
1079\f/*
1080** PRINTADDR -- print address (for debugging)
1081**
1082** Parameters:
1083** a -- the address to print
1084** follow -- follow the q_next chain.
1085**
1086** Returns:
1087** none.
1088**
1089** Side Effects:
1090** none.
1091*/
1092
74c5fe7c
EA
1093# ifdef DEBUG
1094
779597d8
EA
1095printaddr(a, follow)
1096 register ADDRESS *a;
1097 bool follow;
1098{
d4f42161
EA
1099 bool first = TRUE;
1100
abae7b2d
EA
1101 static int indent;
1102 register int i;
1103
779597d8
EA
1104 while (a != NULL)
1105 {
d4f42161 1106 first = FALSE;
abae7b2d
EA
1107 for (i = indent; i > 0; i--)
1108 printf("\t");
331b7c9f 1109 printf("%x=", a);
29871fef 1110 (void) fflush(stdout);
779597d8 1111 printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr,
abae7b2d
EA
1112 for (i = indent; i > 0; i--)
1113 printf("\t");
1114 printf("\tnext=%x, flags=%o, rmailer %d, alias=%x, sibling=%x, child=%x\n",
1115 a->q_next, a->q_flags, a->q_rmailer, a->q_alias,
1116 a->q_sibling, a->q_child);
1117
1118 /* follow the chain if appropriate */
779597d8
EA
1119 if (!follow)
1120 return;
abae7b2d
EA
1121
1122 indent++;
1123 printaddr(a->q_child, TRUE);
1124 indent--;
1125 a = a->q_sibling;
779597d8 1126 }
d4f42161 1127 if (first)
331b7c9f 1128 printf("[NULL]\n");
779597d8 1129}
74c5fe7c
EA
1130
1131# endif DEBUG
22892d62
EA
1132\f/*
1133** REMOTENAME -- return the name relative to the current mailer
1134**
1135** Parameters:
1136** name -- the name to translate.
b00e6882
EA
1137** m -- the mailer that we want to do rewriting relative
1138** to.
1139** senderaddress -- if set, uses the sender rewriting rules
1140** rather than the recipient rewriting rules.
532c9874
EA
1141** canonical -- if set, strip out any comment information,
1142** etc.
22892d62
EA
1143**
1144** Returns:
1145** the text string representing this address relative to
1146** the receiving mailer.
1147**
1148** Side Effects:
1149** none.
1150**
1151** Warnings:
1152** The text string returned is tucked away locally;
1153** copy it if you intend to save it.
1154*/
1155
1156char *
532c9874 1157remotename(name, m, senderaddress, canonical)
22892d62
EA
1158 char *name;
1159 struct mailer *m;
b00e6882 1160 bool senderaddress;
532c9874 1161 bool canonical;
22892d62 1162{
b00e6882
EA
1163 register char **pvp;
1164 char *fancy;
22892d62 1165 extern char *macvalue();
857afefe 1166 char *oldg = macvalue('g', CurEnv);
b00e6882
EA
1167 static char buf[MAXNAME];
1168 char lbuf[MAXNAME];
217a0102 1169 char pvpbuf[PSBUFSIZE];
22892d62 1170 extern char **prescan();
0908f182 1171 extern char *crackaddr();
22892d62 1172
e4b94f39
EA
1173# ifdef DEBUG
1174 if (tTd(12, 1))
1175 printf("remotename(%s)\n", name);
1176# endif DEBUG
1177
4db45bf1
EA
1178 /* don't do anything if we are tagging it as special */
1179 if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
1180 return (name);
1181
22892d62 1182 /*
857afefe
EA
1183 ** Do a heuristic crack of this name to extract any comment info.
1184 ** This will leave the name as a comment and a $g macro.
22892d62
EA
1185 */
1186
532c9874 1187 if (canonical)
a73ae8ac 1188 fancy = "\001g";
532c9874
EA
1189 else
1190 fancy = crackaddr(name);
0908f182 1191
857afefe
EA
1192 /*
1193 ** Turn the name into canonical form.
1194 ** Normally this will be RFC 822 style, i.e., "user@domain".
1195 ** If this only resolves to "user", and the "C" flag is
1196 ** specified in the sending mailer, then the sender's
1197 ** domain will be appended.
1198 */
1199
217a0102 1200 pvp = prescan(name, '\0', pvpbuf);
0908f182 1201 if (pvp == NULL)
22892d62 1202 return (name);
857afefe
EA
1203 rewrite(pvp, 3);
1204 if (CurEnv->e_fromdomain != NULL)
1205 {
1206 /* append from domain to this address */
1207 register char **pxp = pvp;
1208
18c3cee9 1209 /* see if there is an "@domain" in the current name */
857afefe
EA
1210 while (*pxp != NULL && strcmp(*pxp, "@") != 0)
1211 pxp++;
1212 if (*pxp == NULL)
1213 {
18c3cee9 1214 /* no.... append the "@domain" from the sender */
857afefe
EA
1215 register char **qxq = CurEnv->e_fromdomain;
1216
18c3cee9
EA
1217 while ((*pxp++ = *qxq++) != NULL)
1218 continue;
82eade9e 1219 rewrite(pvp, 3);
857afefe
EA
1220 }
1221 }
1222
1223 /*
b726abe3 1224 ** Do more specific rewriting.
857afefe
EA
1225 ** Rewrite using ruleset 1 or 2 depending on whether this is
1226 ** a sender address or not.
1227 ** Then run it through any receiving-mailer-specific rulesets.
1228 */
1229
b00e6882 1230 if (senderaddress)
22892d62 1231 {
0908f182 1232 rewrite(pvp, 1);
b00e6882
EA
1233 if (m->m_s_rwset > 0)
1234 rewrite(pvp, m->m_s_rwset);
1235 }
1236 else
1237 {
0908f182 1238 rewrite(pvp, 2);
b00e6882
EA
1239 if (m->m_r_rwset > 0)
1240 rewrite(pvp, m->m_r_rwset);
0908f182 1241 }
22892d62 1242
b726abe3
EA
1243 /*
1244 ** Do any final sanitation the address may require.
1245 ** This will normally be used to turn internal forms
1246 ** (e.g., user@host.LOCAL) into external form. This
1247 ** may be used as a default to the above rules.
1248 */
1249
1250 rewrite(pvp, 4);
1251
857afefe
EA
1252 /*
1253 ** Now restore the comment information we had at the beginning.
1254 */
1255
0908f182 1256 cataddr(pvp, lbuf, sizeof lbuf);
7338e3d4 1257 define('g', lbuf, CurEnv);
0908f182 1258 expand(fancy, buf, &buf[sizeof buf - 1], CurEnv);
7338e3d4 1259 define('g', oldg, CurEnv);
22892d62
EA
1260
1261# ifdef DEBUG
1262 if (tTd(12, 1))
e4b94f39 1263 printf("remotename => `%s'\n", buf);
22892d62
EA
1264# endif DEBUG
1265 return (buf);
1266}