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