add -q map flag to avoid quote stripping; clean up domain addition
[unix-history] / usr / src / usr.sbin / sendmail / src / parseaddr.c
CommitLineData
f0e070dc 1/*
792e6158 2 * Copyright (c) 1983, 1995 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
513d97f3 10static char sccsid[] = "@(#)parseaddr.c 8.69 (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;
5c5d2382 1708 register char *p;
51d9cc47 1709 char *bp;
5c5d2382
EA
1710 char *mname;
1711 char **hostp;
1712 char hbuf[MAXNAME + 1];
9577b1df
EA
1713 static MAILER errormailer;
1714 static char *errorargv[] = { "ERROR", NULL };
5c5d2382 1715 static char ubuf[MAXNAME + 1];
d6a28dd8 1716
b07c457d
EA
1717 if (tTd(24, 5))
1718 {
d6474f90 1719 printf("buildaddr, flags=%x, tv=", flags);
b07c457d
EA
1720 printav(tv);
1721 }
1722
d6a28dd8
EA
1723 if (a == NULL)
1724 a = (ADDRESS *) xalloc(sizeof *a);
abae7b2d 1725 clear((char *) a, sizeof *a);
d6a28dd8 1726
68d9129a 1727 /* set up default error return flags */
82e3dc75 1728 a->q_flags |= QPINGONFAILURE|QPINGONDELAY;
68d9129a 1729
d6a28dd8 1730 /* figure out what net/mailer to use */
fd6cfc53 1731 if (*tv == NULL || **tv != CANONNET)
fe43b434 1732 {
b6edea3d 1733 syserr("554 buildaddr: no net");
9577b1df
EA
1734badaddr:
1735 a->q_flags |= QBADADDR;
1736 a->q_mailer = &errormailer;
1737 if (errormailer.m_name == NULL)
1738 {
1739 /* initialize the bogus mailer */
1740 errormailer.m_name = "*error*";
1741 errormailer.m_mailer = "ERROR";
1742 errormailer.m_argv = errorargv;
1743 }
1744 return a;
fe43b434 1745 }
5c5d2382
EA
1746 mname = *++tv;
1747
1748 /* extract host and user portions */
1749 if ((**++tv & 0377) == CANONHOST)
1750 hostp = ++tv;
1751 else
1752 hostp = NULL;
1753 while (*tv != NULL && (**tv & 0377) != CANONUSER)
1754 tv++;
1755 if (*tv == NULL)
fe43b434 1756 {
5c5d2382
EA
1757 syserr("554 buildaddr: no user");
1758 goto badaddr;
1759 }
1760 if (hostp != NULL)
1761 cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0');
1762 cataddr(++tv, NULL, ubuf, sizeof ubuf, '\0');
1763
1764 /* save away the host name */
1765 if (strcasecmp(mname, "error") == 0)
1766 {
1767 if (hostp != NULL)
4fd73cf9 1768 {
c796f255
EA
1769 register struct errcodes *ep;
1770
5c5d2382
EA
1771 if (strchr(hbuf, '.') != NULL)
1772 {
1773 a->q_status = newstr(hbuf);
1774 setstat(dsntoexitstat(hbuf));
1775 }
1776 else if (isascii(hbuf[0]) && isdigit(hbuf[0]))
c796f255 1777 {
5c5d2382 1778 setstat(atoi(hbuf));
c796f255
EA
1779 }
1780 else
1781 {
1782 for (ep = ErrorCodes; ep->ec_name != NULL; ep++)
5c5d2382 1783 if (strcasecmp(ep->ec_name, hbuf) == 0)
c796f255
EA
1784 break;
1785 setstat(ep->ec_code);
1786 }
4fd73cf9 1787 }
b34fac73
EA
1788 else
1789 setstat(EX_UNAVAILABLE);
fd6cfc53
EA
1790 buf[0] = '\0';
1791 for (; (*tv != NULL) && (**tv != CANONUSER); tv++)
1792 {
1793 if (buf[0] != '\0')
1794 (void) strcat(buf, " ");
1795 (void) strcat(buf, *tv);
1796 }
5c5d2382
EA
1797 stripquotes(ubuf);
1798 if (isascii(ubuf[0]) && isdigit(ubuf[0]) &&
1799 isascii(ubuf[1]) && isdigit(ubuf[1]) &&
1800 isascii(ubuf[2]) && isdigit(ubuf[2]) &&
1801 ubuf[3] == ' ')
a10b59b5
EA
1802 {
1803 char fmt[10];
1804
5c5d2382 1805 strncpy(fmt, ubuf, 3);
a10b59b5 1806 strcpy(&fmt[3], " %s");
5c5d2382 1807 usrerr(fmt, ubuf + 4);
486825cf
EA
1808
1809 /*
1810 ** If this is a 4xx code and we aren't running
1811 ** SMTP on our input, bounce this message;
1812 ** otherwise it disappears without a trace.
1813 */
1814
1815 if (fmt[0] == '4' && OpMode != MD_SMTP &&
1816 OpMode != MD_DAEMON)
1817 {
1818 e->e_flags |= EF_FATALERRS;
1819 }
a10b59b5
EA
1820 }
1821 else
1822 {
5c5d2382 1823 usrerr("553 %s", ubuf);
a10b59b5 1824 }
9577b1df 1825 goto badaddr;
fe43b434 1826 }
98e5062b 1827
179c1218 1828 for (mp = Mailer; (m = *mp++) != NULL; )
d6a28dd8 1829 {
5c5d2382 1830 if (strcasecmp(m->m_name, mname) == 0)
d6a28dd8
EA
1831 break;
1832 }
1833 if (m == NULL)
fe43b434 1834 {
5c5d2382 1835 syserr("554 buildaddr: unknown mailer %s", mname);
9577b1df 1836 goto badaddr;
fe43b434 1837 }
179c1218 1838 a->q_mailer = m;
d6a28dd8
EA
1839
1840 /* figure out what host (if any) */
fd6cfc53 1841 if (**++tv != CANONHOST)
d6a28dd8 1842 {
6b3bbc1b
EA
1843 a->q_host = NULL;
1844 }
1845 else
1846 {
fd6cfc53
EA
1847 else
1848 a->q_host = NULL;
1849 }
1850 else
1851 {
d6a28dd8
EA
1852
1853 /* figure out the user */
5c5d2382
EA
1854 p = ubuf;
1855 if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@')
1f116778 1856 {
5c5d2382 1857 p++;
1f116778
EA
1858 tv++;
1859 a->q_flags |= QNOTREMOTE;
1860 }
1861
98e5062b 1862 /* do special mapping for local mailer */
5c5d2382
EA
1863 if (*p == '"')
1864 p++;
1865 if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags))
1866 a->q_mailer = m = ProgMailer;
1867 else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags))
1868 a->q_mailer = m = FileMailer;
1869 else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags))
98e5062b 1870 {
5c5d2382
EA
1871 /* may be :include: */
1872 stripquotes(ubuf);
1873 if (strncasecmp(ubuf, ":include:", 9) == 0)
98e5062b 1874 {
5c5d2382
EA
1875 /* if :include:, don't need further rewriting */
1876 a->q_mailer = m = InclMailer;
1877 a->q_user = newstr(&ubuf[9]);
1878 return a;
98e5062b
EA
1879 }
1880 }
d13779b1 1881
c40bef63
EA
1882 if (m->m_r_rwset > 0)
1883 rewrite(tv, m->m_r_rwset);
b141a9b6 1884 (void) rewrite(tv, 4, 0, e);
5108b0cc
EA
1885
1886 /* save the result for the command line/RCPT argument */
5c5d2382
EA
1887 cataddr(tv, NULL, ubuf, sizeof ubuf, '\0');
1888 a->q_user = ubuf;
d6a28dd8 1889
dd3ec3f5
EA
1890 /*
1891 ** Do mapping to lower case as requested by mailer
1892 */
1893
1894 if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags))
1895 makelower(a->q_host);
1896 if (!bitnset(M_USR_UPPER, m->m_flags))
1897 makelower(a->q_user);
1898
5c5d2382 1899 return a;
d6a28dd8 1900}
9e3c0a28 1901\f/*
31b33174
EA
1902** CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
1903**
1904** Parameters:
1905** pvp -- parameter vector to rebuild.
4fcb2bfa
EA
1906** evp -- last parameter to include. Can be NULL to
1907** use entire pvp.
31b33174
EA
1908** buf -- buffer to build the string into.
1909** sz -- size of buf.
1c7897ef
EA
1910** spacesub -- the space separator character; if null,
1911** use SpaceSub.
31b33174
EA
1912**
1913** Returns:
1914** none.
1915**
1916** Side Effects:
1917** Destroys buf.
1918*/
1919
fd6cfc53 1920void
4fcb2bfa 1921cataddr(pvp, evp, buf, sz, spacesub)
31b33174 1922 char **pvp;
4fcb2bfa 1923 char **evp;
31b33174
EA
1924 char *buf;
1925 register int sz;
1c7897ef 1926 char spacesub;
31b33174
EA
1927{
1928 bool oatomtok = FALSE;
fd6cfc53 1929 bool natomtok;
31b33174
EA
1930 register int i;
1931 register char *p;
1932
1c7897ef
EA
1933 if (spacesub == '\0')
1934 spacesub = SpaceSub;
1935
e93460a9
EA
1936 if (pvp == NULL)
1937 {
03388044 1938 (void) strcpy(buf, "");
e93460a9
EA
1939 return;
1940 }
31b33174 1941 p = buf;
89dd4c8f 1942 sz -= 2;
31b33174
EA
1943 while (*pvp != NULL && (i = strlen(*pvp)) < sz)
1944 {
800c836f 1945 natomtok = (TokTypeTab[**pvp & 0xff] == ATM);
31b33174 1946 if (oatomtok && natomtok)
1c7897ef 1947 *p++ = spacesub;
31b33174
EA
1948 (void) strcpy(p, *pvp);
1949 oatomtok = natomtok;
1950 p += i;
89dd4c8f 1951 sz -= i + 1;
4fcb2bfa
EA
1952 if (pvp++ == evp)
1953 break;
31b33174
EA
1954 }
1955 *p = '\0';
1956}
1957\f/*
9e3c0a28
EA
1958** SAMEADDR -- Determine if two addresses are the same
1959**
1960** This is not just a straight comparison -- if the mailer doesn't
1961** care about the host we just ignore it, etc.
1962**
1963** Parameters:
1964** a, b -- pointers to the internal forms to compare.
9e3c0a28
EA
1965**
1966** Returns:
1967** TRUE -- they represent the same mailbox.
1968** FALSE -- they don't.
1969**
1970** Side Effects:
1971** none.
1972*/
1973
1974bool
7338e3d4 1975sameaddr(a, b)
9e3c0a28
EA
1976 register ADDRESS *a;
1977 register ADDRESS *b;
9e3c0a28 1978{
60d490ff
EA
1979 register ADDRESS *ca, *cb;
1980
9e3c0a28
EA
1981 /* if they don't have the same mailer, forget it */
1982 if (a->q_mailer != b->q_mailer)
1983 return (FALSE);
1984
1985 /* if the user isn't the same, we can drop out */
fd6cfc53 1986 if (strcasecmp(a->q_user, b->q_user))
9e3c0a28
EA
1987 return (FALSE);
1988
60d490ff 1989 /* if we have good uids for both but they differ, these are different */
180a908c
EA
1990 if (a->q_mailer == ProgMailer)
1991 {
1992 ca = getctladdr(a);
1993 cb = getctladdr(b);
1994 if (ca != NULL && cb != NULL &&
1995 bitset(QGOODUID, ca->q_flags & cb->q_flags) &&
1996 ca->q_uid != cb->q_uid)
1997 return (FALSE);
1998 }
4e535265 1999
9e3c0a28 2000 /* otherwise compare hosts (but be careful for NULL ptrs) */
fa085f58
EA
2001 if (a->q_host == b->q_host)
2002 {
2003 /* probably both null pointers */
2004 return (TRUE);
2005 }
9e3c0a28 2006 if (a->q_host == NULL || b->q_host == NULL)
fa085f58
EA
2007 {
2008 /* only one is a null pointer */
9e3c0a28 2009 return (FALSE);
fa085f58 2010 }
fd6cfc53 2011 if (strcasecmp(a->q_host, b->q_host))
9e3c0a28
EA
2012 return (FALSE);
2013
2014 return (TRUE);
2015}
779597d8
EA
2016\f/*
2017** PRINTADDR -- print address (for debugging)
2018**
2019** Parameters:
2020** a -- the address to print
2021** follow -- follow the q_next chain.
2022**
2023** Returns:
2024** none.
2025**
2026** Side Effects:
2027** none.
2028*/
2029
bdd1eb9a
EA
2030struct qflags
2031{
2032 char *qf_name;
2033 u_long qf_bit;
2034};
2035
2036struct qflags AddressFlags[] =
2037{
2038 "QDONTSEND", QDONTSEND,
2039 "QBADADDR", QBADADDR,
2040 "QGOODUID", QGOODUID,
2041 "QPRIMARY", QPRIMARY,
2042 "QQUEUEUP", QQUEUEUP,
2043 "QSENT", QSENT,
2044 "QNOTREMOTE", QNOTREMOTE,
2045 "QSELFREF", QSELFREF,
2046 "QVERIFIED", QVERIFIED,
bdd1eb9a
EA
2047 "QBOGUSSHELL", QBOGUSSHELL,
2048 "QUNSAFEADDR", QUNSAFEADDR,
2049 "QPINGONSUCCESS", QPINGONSUCCESS,
2050 "QPINGONFAILURE", QPINGONFAILURE,
2051 "QPINGONDELAY", QPINGONDELAY,
e1f691b3 2052 "QHASNOTIFY", QHASNOTIFY,
bdd1eb9a 2053 "QRELAYED", QRELAYED,
126c1099
EA
2054 "QEXPANDED", QEXPANDED,
2055 "QDELIVERED", QDELIVERED,
2056 "QDELAYED", QDELAYED,
66d16835 2057 "QTHISPASS", QTHISPASS,
bdd1eb9a
EA
2058 NULL
2059};
2060
ea07b2d2 2061void
fd6cfc53 2062void
779597d8
EA
2063printaddr(a, follow)
2064 register ADDRESS *a;
2065 bool follow;
2066{
78bbbc48
EA
2067 register MAILER *m;
2068 MAILER pseudomailer;
bdd1eb9a
EA
2069 register struct qflags *qfp;
2070 bool firstone;
2071
2072 if (a == NULL)
2073 {
2074 printf("[NULL]\n");
2075 return;
2076 }
d4f42161 2077
abae7b2d
EA
2078 static int indent;
2079 register int i;
2080
779597d8
EA
2081 while (a != NULL)
2082 {
abae7b2d
EA
2083 for (i = indent; i > 0; i--)
2084 printf("\t");
331b7c9f 2085 printf("%x=", a);
29871fef 2086 (void) fflush(stdout);
78bbbc48
EA
2087
2088 /* find the mailer -- carefully */
2089 m = a->q_mailer;
2090 if (m == NULL)
2091 {
2092 m = &pseudomailer;
2093 m->m_mno = -1;
2094 m->m_name = "NULL";
2095 }
2096
abae7b2d
EA
2097 for (i = indent; i > 0; i--)
2098 printf("\t");
2099 printf("\tnext=%x, flags=%o, rmailer %d, alias=%x, sibling=%x, child=%x\n",
2100 a->q_next, a->q_flags, a->q_rmailer, a->q_alias,
2101 a->q_sibling, a->q_child);
2102
2103 /* follow the chain if appropriate */
779597d8
EA
2104 if (!follow)
2105 return;
abae7b2d
EA
2106
2107 indent++;
2108 printaddr(a->q_child, TRUE);
2109 indent--;
2110 a = a->q_sibling;
779597d8
EA
2111 }
2112}
f43d47fc
EA
2113\f/*
2114** EMPTYADDR -- return TRUE if this address is empty (``<>'')
2115**
2116** Parameters:
2117** a -- pointer to the address
2118**
2119** Returns:
2120** TRUE -- if this address is "empty" (i.e., no one should
2121** ever generate replies to it.
2122** FALSE -- if it is a "regular" (read: replyable) address.
2123*/
74c5fe7c 2124
f43d47fc
EA
2125bool
2126emptyaddr(a)
2127 register ADDRESS *a;
2128{
2129 return strcmp(a->q_paddr, "<>") == 0 || strcmp(a->q_user, "<>") == 0;
2130}
22892d62
EA
2131\f/*
2132** REMOTENAME -- return the name relative to the current mailer
2133**
2134** Parameters:
2135** name -- the name to translate.
b00e6882
EA
2136** m -- the mailer that we want to do rewriting relative
2137** to.
9e43a19e
EA
2138** flags -- fine tune operations.
2139** pstat -- pointer to status word.
68f7099c 2140** e -- the current envelope.
22892d62
EA
2141**
2142** Returns:
2143** the text string representing this address relative to
2144** the receiving mailer.
2145**
2146** Side Effects:
2147** none.
2148**
2149** Warnings:
2150** The text string returned is tucked away locally;
2151** copy it if you intend to save it.
2152*/
2153
2154char *
9e43a19e 2155remotename(name, m, flags, pstat, e)
22892d62 2156 char *name;
fd6cfc53 2157 MAILER *m;
9e43a19e
EA
2158 int flags;
2159 int *pstat;
a4076aed 2160 register ENVELOPE *e;
22892d62 2161{
b00e6882
EA
2162 register char **pvp;
2163 char *fancy;
a4076aed 2164 char *oldg = macvalue('g', e);
68f7099c 2165 int rwset;
8446c922
EA
2166 static char buf[MAXNAME + 1];
2167 char lbuf[MAXNAME + 1];
217a0102 2168 char pvpbuf[PSBUFSIZE];
22892d62 2169
e4b94f39
EA
2170 if (tTd(12, 1))
2171 printf("remotename(%s)\n", name);
e4b94f39 2172
4db45bf1 2173 /* don't do anything if we are tagging it as special */
c40bef63 2174 if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
4db45bf1
EA
2175 return (name);
2176
22892d62 2177 /*
857afefe
EA
2178 ** Do a heuristic crack of this name to extract any comment info.
2179 ** This will leave the name as a comment and a $g macro.
22892d62
EA
2180 */
2181
9e43a19e 2182 if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags))
2bee003d 2183 fancy = "\201g";
532c9874
EA
2184 else
2185 fancy = crackaddr(name);
0908f182 2186
857afefe
EA
2187 /*
2188 ** Turn the name into canonical form.
2189 ** Normally this will be RFC 822 style, i.e., "user@domain".
2190 ** If this only resolves to "user", and the "C" flag is
2191 ** specified in the sending mailer, then the sender's
2192 ** domain will be appended.
2193 */
2194
800c836f 2195 pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL);
0908f182 2196 if (pvp == NULL)
22892d62 2197 return (name);
b141a9b6 2198 if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
9e43a19e
EA
2199 *pstat = EX_TEMPFAIL;
2200 if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL)
857afefe
EA
2201 {
2202 /* append from domain to this address */
2203 register char **pxp = pvp;
2204
18c3cee9 2205 /* see if there is an "@domain" in the current name */
857afefe
EA
2206 while (*pxp != NULL && strcmp(*pxp, "@") != 0)
2207 pxp++;
2208 if (*pxp == NULL)
2209 {
18c3cee9 2210 /* no.... append the "@domain" from the sender */
a4076aed 2211 register char **qxq = e->e_fromdomain;
857afefe 2212
18c3cee9
EA
2213 while ((*pxp++ = *qxq++) != NULL)
2214 continue;
b141a9b6 2215 if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL)
9e43a19e 2216 *pstat = EX_TEMPFAIL;
857afefe
EA
2217 }
2218 }
2219
2220 /*
b726abe3 2221 ** Do more specific rewriting.
fd6cfc53
EA
2222 ** Rewrite using ruleset 1 or 2 for envelope addresses and
2223 ** 5 or 6 for header addresses depending on whether this
2224 ** is a sender address or not.
857afefe
EA
2225 ** Then run it through any receiving-mailer-specific rulesets.
2226 */
2227
b00e6882 2228 else
9e4c75d5 2229 {
68f7099c 2230 if (rwset > 0)
9e4c75d5 2231 {
b141a9b6 2232 if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL)
9e43a19e 2233 *pstat = EX_TEMPFAIL;
9e4c75d5 2234 }
22892d62 2235
b726abe3
EA
2236 /*
2237 ** Do any final sanitation the address may require.
2238 ** This will normally be used to turn internal forms
2239 ** (e.g., user@host.LOCAL) into external form. This
2240 ** may be used as a default to the above rules.
2241 */
2242
b141a9b6 2243 if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL)
9e43a19e 2244 *pstat = EX_TEMPFAIL;
b726abe3 2245
857afefe
EA
2246 /*
2247 ** Now restore the comment information we had at the beginning.
2248 */
2249
9d6aecfd 2250 cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0');
a4076aed 2251 define('g', lbuf, e);
61fabf2e
EA
2252
2253 /* need to make sure route-addrs have <angle brackets> */
2254 if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@')
832e8a27 2255 expand("<\201g>", buf, sizeof buf, e);
61fabf2e 2256 else
832e8a27 2257 expand(fancy, buf, sizeof buf, e);
61fabf2e 2258
a4076aed 2259 define('g', oldg, e);
22892d62 2260
22892d62 2261 if (tTd(12, 1))
e4b94f39 2262 printf("remotename => `%s'\n", buf);
22892d62
EA
2263 return (buf);
2264}
d13779b1 2265\f/*
fd6cfc53
EA
2266** UURELATIVIZE -- Make an address !-relative to recipient/sender nodes
2267**
2268** Parameters:
2269** from -- the sending node (usually "$k" or "$w")
2270** to -- the receiving node (usually "$h")
2271** pvp -- address vector
2272**
2273** Returns:
2274** none.
2275**
2276** Side Effects:
2277** The pvp is rewritten to be relative the "to" node
2278** wrt the "from" node. In other words, if the pvp
2279** is headed by "to!" that part is stripped; otherwise
2280** "from!" is prepended. Exception: "to!user" addresses
2281** with no '!'s in the user part are sent as is.
2282**
2283** Bugs:
2284** The pvp may overflow, but we don't catch it.
2285*/
2286
2287static void
2288uurelativize(from, to, pvp)
2289 const char *from, *to;
2290 char **pvp;
2291{
2292 register char **pxp = pvp;
2293 char expfrom[MAXNAME], expto[MAXNAME];
2294
2295 expand(from, expfrom, &expfrom[sizeof expfrom - 1], CurEnv);
2296 expand(to, expto, &expto[sizeof expto - 1], CurEnv);
2297
2298 /*
2299 * supposing that we've got something, should
2300 * we add "from!" or remove "to!"?
2301 */
2302 if (pvp[0] != NULL)
2303 if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 ||
2304 /*strcasecmp?*/ strcmp(pvp[0], expto) != 0)
2305 {
2306 /* either local name, no UUCP address, */
2307 /* or not to "to!" ==> prepend address with "from!" */
2308
2309 /* already there? */
2310 if (pvp[1] == NULL || strcmp(pvp[1], "!") != 0 ||
2311 /*strcasecmp?*/ strcmp(pvp[0], expfrom) != 0)
2312 {
2313
2314 /* no, put it there */
2315 while (*pxp != NULL)
2316 pxp++;
2317 do
2318 pxp[2] = *pxp;
2319 while (pxp-- != pvp);
2320 pvp[0] = newstr(expfrom);
2321 pvp[1] = "!";
2322 }
2323 }
2324 else
2325 {
2326 /* address is to "to!" -- remove if not "to!user" */
2327 for (pxp = &pvp[2];
2328 *pxp != NULL && strcmp(*pxp, "!") != 0; pxp++)
2329 ;
2330 if (*pxp != NULL)
2331 for (pxp = pvp; *pxp != NULL; pxp++)
2332 *pxp = pxp[2];
2333 }
2334}
2335\f/*
d13779b1
EA
2336** MAPLOCALUSER -- run local username through ruleset 5 for final redirection
2337**
2338** Parameters:
2339** a -- the address to map (but just the user name part).
2340** sendq -- the sendq in which to install any replacement
2341** addresses.
f8c2f9fd
EA
2342** aliaslevel -- the alias nesting depth.
2343** e -- the envelope.
d13779b1
EA
2344**
2345** Returns:
2346** none.
2347*/
2348
f8c2f9fd 2349maplocaluser(a, sendq, aliaslevel, e)
d13779b1
EA
2350 register ADDRESS *a;
2351 ADDRESS **sendq;
f8c2f9fd 2352 int aliaslevel;
a4076aed 2353 ENVELOPE *e;
d13779b1
EA
2354{
2355 register char **pvp;
2356 register ADDRESS *a1 = NULL;
9e2cf26f 2357 auto char *delimptr;
d13779b1
EA
2358 char pvpbuf[PSBUFSIZE];
2359
2360 if (tTd(29, 1))
2361 {
2362 printf("maplocaluser: ");
2363 printaddr(a, FALSE);
2364 }
800c836f 2365 pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
d13779b1
EA
2366 if (pvp == NULL)
2367 return;
2368
b141a9b6 2369 (void) rewrite(pvp, 5, 0, e);
2bee003d 2370 if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
d13779b1
EA
2371 return;
2372
2373 /* if non-null, mailer destination specified -- has it changed? */
28f94061 2374 a1 = buildaddr(pvp, NULL, 0, e);
d13779b1
EA
2375 if (a1 == NULL || sameaddr(a, a1))
2376 return;
2377
2378 /* mark old address as dead; insert new address */
2379 a->q_flags |= QDONTSEND;
78bbbc48
EA
2380 if (tTd(29, 5))
2381 {
2382 printf("maplocaluser: QDONTSEND ");
2383 printaddr(a, FALSE);
2384 }
d13779b1 2385 a1->q_alias = a;
dab5fedb 2386 allocaddr(a1, RF_COPYALL, NULL);
f8c2f9fd 2387 (void) recipient(a1, sendq, aliaslevel, e);
d13779b1 2388}
28d46d78
EA
2389\f/*
2390** DEQUOTE_INIT -- initialize dequote map
2391**
2392** This is a no-op.
2393**
2394** Parameters:
2395** map -- the internal map structure.
28d46d78
EA
2396** args -- arguments.
2397**
2398** Returns:
2399** TRUE.
2400*/
2401
2402bool
9114f86c 2403dequote_init(map, args)
28d46d78 2404 MAP *map;
28d46d78
EA
2405 char *args;
2406{
5111538e
EA
2407 register char *p = args;
2408
2409 for (;;)
2410 {
2411 while (isascii(*p) && isspace(*p))
2412 p++;
2413 if (*p != '-')
2414 break;
2415 switch (*++p)
2416 {
2417 case 'a':
2418 map->map_app = ++p;
2419 break;
4cfe7b49
EA
2420
2421 case 's':
2422 map->map_coldelim = *++p;
2423 break;
5111538e
EA
2424 }
2425 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2426 p++;
2427 if (*p != '\0')
2428 *p = '\0';
2429 }
2430 if (map->map_app != NULL)
2431 map->map_app = newstr(map->map_app);
2432
28d46d78
EA
2433 return TRUE;
2434}
2435\f/*
2436** DEQUOTE_MAP -- unquote an address
2437**
2438** Parameters:
2439** map -- the internal map structure (ignored).
713c523f 2440** name -- the name to dequote.
28d46d78 2441** av -- arguments (ignored).
d1db7a89 2442** statp -- pointer to status out-parameter.
28d46d78
EA
2443**
2444** Returns:
2445** NULL -- if there were no quotes, or if the resulting
2446** unquoted buffer would not be acceptable to prescan.
2447** else -- The dequoted buffer.
2448*/
2449
2450char *
713c523f 2451dequote_map(map, name, av, statp)
28d46d78 2452 MAP *map;
713c523f 2453 char *name;
28d46d78 2454 char **av;
d1db7a89 2455 int *statp;
28d46d78
EA
2456{
2457 register char *p;
2458 register char *q;
2459 register char c;
4cfe7b49
EA
2460 int anglecnt = 0;
2461 int cmntcnt = 0;
2462 int quotecnt = 0;
2463 int spacecnt = 0;
2464 bool quotemode = FALSE;
2465 bool bslashmode = FALSE;
2466 char spacesub = map->map_coldelim;
28d46d78 2467
713c523f 2468 for (p = q = name; (c = *p++) != '\0'; )
28d46d78
EA
2469 {
2470 if (bslashmode)
2471 {
2472 bslashmode = FALSE;
2473 *q++ = c;
2474 continue;
2475 }
2476
4cfe7b49
EA
2477 if (c == ' ' && spacesub != '\0')
2478 c = spacesub;
aff9529d 2479
28d46d78
EA
2480 switch (c)
2481 {
2482 case '\\':
2483 bslashmode = TRUE;
2484 break;
2485
2486 case '(':
2487 cmntcnt++;
2488 break;
2489
2490 case ')':
2491 if (cmntcnt-- <= 0)
2492 return NULL;
2493 break;
493ddf6a
EA
2494
2495 case ' ':
2496 spacecnt++;
2497 break;
28d46d78
EA
2498 }
2499
2500 if (cmntcnt > 0)
2501 {
2502 *q++ = c;
2503 continue;
2504 }
2505
2506 switch (c)
2507 {
2508 case '"':
2509 quotemode = !quotemode;
2510 quotecnt++;
2511 continue;
2512
2513 case '<':
2514 anglecnt++;
2515 break;
2516
2517 case '>':
2518 if (anglecnt-- <= 0)
2519 return NULL;
2520 break;
2521 }
2522 *q++ = c;
2523 }
2524
2525 if (anglecnt != 0 || cmntcnt != 0 || bslashmode ||
493ddf6a 2526 quotemode || quotecnt <= 0 || spacecnt != 0)
28d46d78
EA
2527 return NULL;
2528 *q++ = '\0';
713c523f 2529 return name;
28d46d78 2530}