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