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