Start
[exim.git] / src / src / routers / redirect.c
CommitLineData
0756eb3c
PH
1/* $Cambridge: exim/src/src/routers/redirect.c,v 1.1 2004/10/07 13:10:02 ph10 Exp $ */
2
3/*************************************************
4* Exim - an Internet mail transport agent *
5*************************************************/
6
7/* Copyright (c) University of Cambridge 1995 - 2004 */
8/* See the file NOTICE for conditions of use and distribution. */
9
10
11#include "../exim.h"
12#include "rf_functions.h"
13#include "redirect.h"
14
15
16
17/* Options specific to the redirect router. */
18
19optionlist redirect_router_options[] = {
20 { "allow_defer", opt_bit | (RDON_DEFER << 16),
21 (void *)offsetof(redirect_router_options_block, bit_options) },
22 { "allow_fail", opt_bit | (RDON_FAIL << 16),
23 (void *)offsetof(redirect_router_options_block, bit_options) },
24 { "allow_filter", opt_bit | (RDON_FILTER << 16),
25 (void *)offsetof(redirect_router_options_block, bit_options) },
26 { "allow_freeze", opt_bit | (RDON_FREEZE << 16),
27 (void *)offsetof(redirect_router_options_block, bit_options) },
28 { "check_ancestor", opt_bool,
29 (void *)offsetof(redirect_router_options_block, check_ancestor) },
30 { "check_group", opt_bool,
31 (void *)offsetof(redirect_router_options_block, check_group) },
32 { "check_owner", opt_bool,
33 (void *)offsetof(redirect_router_options_block, check_owner) },
34 { "data", opt_stringptr,
35 (void *)offsetof(redirect_router_options_block, data) },
36 { "directory_transport",opt_stringptr,
37 (void *)offsetof(redirect_router_options_block, directory_transport_name) },
38 { "file", opt_stringptr,
39 (void *)offsetof(redirect_router_options_block, file) },
40 { "file_transport", opt_stringptr,
41 (void *)offsetof(redirect_router_options_block, file_transport_name) },
42 { "forbid_blackhole", opt_bit | (RDON_BLACKHOLE << 16),
43 (void *)offsetof(redirect_router_options_block, bit_options) },
44 { "forbid_file", opt_bool,
45 (void *)offsetof(redirect_router_options_block, forbid_file) },
46 { "forbid_filter_existstest", opt_bit | (RDON_EXISTS << 16),
47 (void *)offsetof(redirect_router_options_block, bit_options) },
48 { "forbid_filter_logwrite",opt_bit | (RDON_LOG << 16),
49 (void *)offsetof(redirect_router_options_block, bit_options) },
50 { "forbid_filter_lookup", opt_bit | (RDON_LOOKUP << 16),
51 (void *)offsetof(redirect_router_options_block, bit_options) },
52 #ifdef EXIM_PERL
53 { "forbid_filter_perl", opt_bit | (RDON_PERL << 16),
54 (void *)offsetof(redirect_router_options_block, bit_options) },
55 #endif
56 { "forbid_filter_readfile", opt_bit | (RDON_READFILE << 16),
57 (void *)offsetof(redirect_router_options_block, bit_options) },
58 { "forbid_filter_readsocket", opt_bit | (RDON_READSOCK << 16),
59 (void *)offsetof(redirect_router_options_block, bit_options) },
60 { "forbid_filter_reply",opt_bool,
61 (void *)offsetof(redirect_router_options_block, forbid_filter_reply) },
62 { "forbid_filter_run", opt_bit | (RDON_RUN << 16),
63 (void *)offsetof(redirect_router_options_block, bit_options) },
64 { "forbid_include", opt_bit | (RDON_INCLUDE << 16),
65 (void *)offsetof(redirect_router_options_block, bit_options) },
66 { "forbid_pipe", opt_bool,
67 (void *)offsetof(redirect_router_options_block, forbid_pipe) },
68 { "hide_child_in_errmsg", opt_bool,
69 (void *)offsetof(redirect_router_options_block, hide_child_in_errmsg) },
70 { "ignore_eacces", opt_bit | (RDON_EACCES << 16),
71 (void *)offsetof(redirect_router_options_block, bit_options) },
72 { "ignore_enotdir", opt_bit | (RDON_ENOTDIR << 16),
73 (void *)offsetof(redirect_router_options_block, bit_options) },
74 { "include_directory", opt_stringptr,
75 (void *)offsetof(redirect_router_options_block, include_directory) },
76 { "modemask", opt_octint,
77 (void *)offsetof(redirect_router_options_block, modemask) },
78 { "one_time", opt_bool,
79 (void *)offsetof(redirect_router_options_block, one_time) },
80 { "owners", opt_uidlist,
81 (void *)offsetof(redirect_router_options_block, owners) },
82 { "owngroups", opt_gidlist,
83 (void *)offsetof(redirect_router_options_block, owngroups) },
84 { "pipe_transport", opt_stringptr,
85 (void *)offsetof(redirect_router_options_block, pipe_transport_name) },
86 { "qualify_domain", opt_stringptr,
87 (void *)offsetof(redirect_router_options_block, qualify_domain) },
88 { "qualify_preserve_domain", opt_bool,
89 (void *)offsetof(redirect_router_options_block, qualify_preserve_domain) },
90 { "repeat_use", opt_bool | opt_public,
91 (void *)offsetof(router_instance, repeat_use) },
92 { "reply_transport", opt_stringptr,
93 (void *)offsetof(redirect_router_options_block, reply_transport_name) },
94 { "rewrite", opt_bit | (RDON_REWRITE << 16),
95 (void *)offsetof(redirect_router_options_block, bit_options) },
96 { "sieve_vacation_directory", opt_stringptr,
97 (void *)offsetof(redirect_router_options_block, sieve_vacation_directory) },
98 { "skip_syntax_errors", opt_bool,
99 (void *)offsetof(redirect_router_options_block, skip_syntax_errors) },
100 { "syntax_errors_text", opt_stringptr,
101 (void *)offsetof(redirect_router_options_block, syntax_errors_text) },
102 { "syntax_errors_to", opt_stringptr,
103 (void *)offsetof(redirect_router_options_block, syntax_errors_to) }
104};
105
106/* Size of the options list. An extern variable has to be used so that its
107address can appear in the tables drtables.c. */
108
109int redirect_router_options_count =
110 sizeof(redirect_router_options)/sizeof(optionlist);
111
112/* Default private options block for the redirect router. */
113
114redirect_router_options_block redirect_router_option_defaults = {
115 NULL, /* directory_transport */
116 NULL, /* file_transport */
117 NULL, /* pipe_transport */
118 NULL, /* reply_transport */
119 NULL, /* data */
120 NULL, /* directory_transport_name */
121 NULL, /* file */
122 NULL, /* file_dir */
123 NULL, /* file_transport_name */
124 NULL, /* include_directory */
125 NULL, /* pipe_transport_name */
126 NULL, /* reply_transport_name */
127 NULL, /* sieve_vacation_directory */
128 NULL, /* syntax_errors_text */
129 NULL, /* syntax_errors_to */
130 NULL, /* qualify_domain */
131 NULL, /* owners */
132 NULL, /* owngroups */
133 022, /* modemask */
134 RDO_REWRITE, /* bit_options */
135 FALSE, /* check_ancestor */
136 TRUE_UNSET, /* check_owner */
137 TRUE_UNSET, /* check_group */
138 FALSE, /* forbid_file */
139 FALSE, /* forbid_filter_reply */
140 FALSE, /* forbid_pipe */
141 FALSE, /* hide_child_in_errmsg */
142 FALSE, /* one_time */
143 FALSE, /* qualify_preserve_domain */
144 FALSE /* skip_syntax_errors */
145};
146
147
148
149/*************************************************
150* Initialization entry point *
151*************************************************/
152
153/* Called for each instance, after its options have been read, to enable
154consistency checks to be done, or anything else that needs to be set up. */
155
156void redirect_router_init(router_instance *rblock)
157{
158redirect_router_options_block *ob =
159 (redirect_router_options_block *)(rblock->options_block);
160
161/* Either file or data must be set, but not both */
162
163if ((ob->file == NULL) == (ob->data == NULL))
164 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
165 "%sone of \"file\" or \"data\" must be specified",
166 rblock->name, (ob->file == NULL)? "" : "only ");
167
168/* Onetime aliases can only be real addresses. Headers can't be manipulated. */
169
170if (ob->one_time)
171 {
172 ob->forbid_pipe = ob->forbid_file = ob->forbid_filter_reply = TRUE;
173 if (rblock->extra_headers != NULL || rblock->remove_headers != NULL)
174 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
175 "\"headers_add\" and \"headers_remove\" are not permitted with "
176 "\"one_time\"", rblock->name);
177 }
178
179/* The defaults for check_owner and check_group depend on other settings. The
180defaults are: Check the owner if check_local_user or owners is set; check the
181group if check_local_user is set without a restriction on the group write bit,
182or if owngroups is set. */
183
184if (ob->check_owner == TRUE_UNSET)
185 ob->check_owner = rblock->check_local_user ||
186 (ob->owners != NULL && ob->owners[0] != 0);
187
188if (ob->check_group == TRUE_UNSET)
189 ob->check_group = (rblock->check_local_user && (ob->modemask & 020) == 0) ||
190 (ob->owngroups != NULL && ob->owngroups[0] != 0);
191
192/* If explicit qualify domain set, the preserve option is locked out */
193
194if (ob->qualify_domain != NULL && ob->qualify_preserve_domain)
195 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
196 "only one of \"qualify_domain\" or \"qualify_preserve_domain\" must be set",
197 rblock->name);
198
199/* If allow_filter is set, either user or check_local_user must be set. */
200
201if (!rblock->check_local_user &&
202 !rblock->uid_set &&
203 rblock->expand_uid == NULL &&
204 (ob->bit_options & RDO_FILTER) != 0)
205 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
206 "\"user\" or \"check_local_user\" must be set with \"allow_filter\"",
207 rblock->name);
208}
209
210
211
212/*************************************************
213* Get errors address and header mods *
214*************************************************/
215
216/* This function is called when new addresses are generated, in order to
217sort out errors address and header modifications. We put the errors address
218into the parent address (even though it is never used from there because that
219address is never transported) so that it can be retrieved if any of the
220children gets routed by an "unseen" router. The clone of the child that is
221passed on must have the original errors_address value.
222
223Arguments:
224 rblock the router control block
225 addr the address being routed
226 verify true if verifying
227 addr_prop point to the propagated block, which is where the
228 new values are to be placed
229
230Returns: the result of rf_get_errors_address() or rf_get_munge_headers(),
231 which is either OK or DEFER
232*/
233
234static int
235sort_errors_and_headers(router_instance *rblock, address_item *addr,
236 BOOL verify, address_item_propagated *addr_prop)
237{
238int frc = rf_get_errors_address(addr, rblock, verify,
239 &(addr_prop->errors_address));
240if (frc != OK) return frc;
241addr->p.errors_address = addr_prop->errors_address;
242return rf_get_munge_headers(addr, rblock, &(addr_prop->extra_headers),
243 &(addr_prop->remove_headers));
244}
245
246
247
248/*************************************************
249* Process a set of generated new addresses *
250*************************************************/
251
252/* This function sets up a set of newly generated child addresses and puts them
253on the new address chain. Copy in the uid, gid and permission flags for use by
254pipes and files, set the parent, and "or" its af_ignore_error flag. Also record
255the setting for any starting router.
256
257If the generated address is the same as one of its ancestors, and the
258check_ancestor flag is set, do not use this generated address, but replace it
259with a copy of the input address. This is to cope with cases where A is aliased
260to B and B has a .forward file pointing to A, though it is usually set on the
261forwardfile rather than the aliasfile. We can't just pass on the old
262address by returning FAIL, because it must act as a general parent for
263generated addresses, and only get marked "done" when all its children are
264delivered.
265
266Arguments:
267 rblock router block
268 addr_new new address chain
269 addr original address
270 generated list of generated addresses
271 addr_prop the propagated block, containing the errors_address,
272 header modification stuff, and address_data
273 ugidptr points to uid/gid data for files, pipes, autoreplies
274 pw password entry, set if ob->check_local_user is TRUE
275
276Returns: nothing
277*/
278
279static void
280add_generated(router_instance *rblock, address_item **addr_new,
281 address_item *addr, address_item *generated,
282 address_item_propagated *addr_prop, ugid_block *ugidptr, struct passwd *pw)
283{
284redirect_router_options_block *ob =
285 (redirect_router_options_block *)(rblock->options_block);
286
287while (generated != NULL)
288 {
289 address_item *parent;
290 address_item *next = generated;
291 uschar *errors_address = next->p.errors_address;
292
293 generated = next->next;
294 next->parent = addr;
295 orflag(next, addr, af_ignore_error);
296 next->start_router = rblock->redirect_router;
297 addr->child_count++;
298
299 next->next = *addr_new;
300 *addr_new = next;
301
302 /* Don't do the "one_time" thing for the first pass of a 2-stage queue run. */
303
304 if (ob->one_time && !queue_2stage)
305 {
306 for (parent = addr; parent->parent != NULL; parent = parent->parent);
307 next->onetime_parent = parent->address;
308 }
309
310 if (ob->hide_child_in_errmsg) setflag(next, af_hide_child);
311
312 /* If check_ancestor is set, we want to know if any ancestor of this address
313 is the address we are about to generate. The check must be done caselessly
314 unless the ancestor was routed by a case-sensitive router. */
315
316 if (ob->check_ancestor)
317 {
318 for (parent = addr; parent != NULL; parent = parent->parent)
319 {
320 if (((parent->router != NULL && parent->router->caseful_local_part)?
321 Ustrcmp(next->address, parent->address)
322 :
323 strcmpic(next->address, parent->address)
324 ) == 0)
325 {
326 DEBUG(D_route) debug_printf("generated parent replaced by child\n");
327 next->address = string_copy(addr->address);
328 break;
329 }
330 }
331 }
332
333 /* A user filter may, under some circumstances, set up an errors address.
334 If so, we must take care to re-instate it when we copy in the propagated
335 data so that it overrides any errors_to setting on the router. */
336
337 next->p = *addr_prop;
338 if (errors_address != NULL) next->p.errors_address = errors_address;
339
340 /* For pipes, files, and autoreplies, record this router as handling them,
341 because they don't go through the routing process again. Then set up uid,
342 gid, home and current directories for transporting. */
343
344 if (testflag(next, af_pfr))
345 {
346 next->router = rblock;
347 rf_set_ugid(next, ugidptr); /* Will contain pw values if not overridden */
348
349 /* When getting the home directory out of the password information, wrap it
350 in \N...\N to avoid expansion later. In Cygwin, home directories can
351 contain $ characters. */
352
353 if (rblock->home_directory != NULL)
354 next->home_dir = rblock->home_directory;
355 else if (rblock->check_local_user)
356 next->home_dir = string_sprintf("\\N%s\\N", pw->pw_dir);
357 else if (rblock->router_home_directory != NULL &&
358 testflag(addr, af_home_expanded))
359 {
360 next->home_dir = deliver_home;
361 setflag(next, af_home_expanded);
362 }
363
364 next->current_dir = rblock->current_directory;
365
366 /* Permission options */
367
368 if (!ob->forbid_pipe) setflag(next, af_allow_pipe);
369 if (!ob->forbid_file) setflag(next, af_allow_file);
370 if (!ob->forbid_filter_reply) setflag(next, af_allow_reply);
371
372 /* If the transport setting fails, the error gets picked up at the outer
373 level from the setting of basic_errno in the address. */
374
375 if (next->address[0] == '|')
376 {
377 address_pipe = next->address;
378 if (rf_get_transport(ob->pipe_transport_name, &(ob->pipe_transport),
379 next, rblock->name, US"pipe_transport"))
380 next->transport = ob->pipe_transport;
381 address_pipe = NULL;
382 }
383 else if (next->address[0] == '>')
384 {
385 if (rf_get_transport(ob->reply_transport_name, &(ob->reply_transport),
386 next, rblock->name, US"reply_transport"))
387 next->transport = ob->reply_transport;
388 }
389 else /* must be file or directory */
390 {
391 int len = Ustrlen(next->address);
392 address_file = next->address;
393 if (next->address[len-1] == '/')
394 {
395 if (rf_get_transport(ob->directory_transport_name,
396 &(ob->directory_transport), next, rblock->name,
397 US"directory_transport"))
398 next->transport = ob->directory_transport;
399 }
400 else
401 {
402 if (rf_get_transport(ob->file_transport_name, &(ob->file_transport),
403 next, rblock->name, US"file_transport"))
404 next->transport = ob->file_transport;
405 }
406 address_file = NULL;
407 }
408 }
409
410 DEBUG(D_route)
411 {
412 debug_printf("%s router generated %s\n %serrors_to=%s transport=%s\n",
413 rblock->name,
414 next->address,
415 testflag(next, af_pfr)? "pipe, file, or autoreply\n " : "",
416 next->p.errors_address,
417 (next->transport == NULL)? US"NULL" : next->transport->name);
418
419 if (testflag(next, af_uid_set))
420 debug_printf(" uid=%ld ", (long int)(next->uid));
421 else
422 debug_printf(" uid=unset ");
423
424 if (testflag(next, af_gid_set))
425 debug_printf("gid=%ld ", (long int)(next->gid));
426 else
427 debug_printf("gid=unset ");
428
429 debug_printf("home=%s\n", next->home_dir);
430 }
431 }
432}
433
434
435/*************************************************
436* Main entry point *
437*************************************************/
438
439/* See local README for interface description. This router returns:
440
441DECLINE
442 . empty address list, or filter did nothing significant
443
444DEFER
445 . verifying the errors address caused a deferment or a big disaster such
446 as an expansion failure (rf_get_errors_address)
447 . expanding a headers_{add,remove} string caused a deferment or another
448 expansion error (rf_get_munge_headers)
449 . :defer: or "freeze" in a filter
450 . error in address list or filter
451 . skipped syntax errors, but failed to send the message
452
453DISCARD
454 . address was :blackhole:d or "seen finish"ed
455
456FAIL
457 . :fail:
458
459OK
460 . new addresses added to addr_new
461*/
462
463int redirect_router_entry(
464 router_instance *rblock, /* data for this instantiation */
465 address_item *addr, /* address we are working on */
466 struct passwd *pw, /* passwd entry after check_local_user */
467 BOOL verify, /* TRUE when verifying */
468 address_item **addr_local, /* add it to this if it's local */
469 address_item **addr_remote, /* add it to this if it's remote */
470 address_item **addr_new, /* put new addresses on here */
471 address_item **addr_succeed) /* put old address here on success */
472{
473redirect_router_options_block *ob =
474 (redirect_router_options_block *)(rblock->options_block);
475address_item *generated = NULL;
476uschar *save_qualify_domain_recipient = qualify_domain_recipient;
477uschar *discarded = US"discarded";
478address_item_propagated addr_prop;
479error_block *eblock = NULL;
480ugid_block ugid;
481redirect_block redirect;
482int filtertype = FILTER_UNSET;
483int yield = OK;
484int options = ob->bit_options;
485int frc = 0;
486int xrc = 0;
487
488addr_local = addr_local; /* Keep picky compilers happy */
489addr_remote = addr_remote;
490
491/* Initialize the data to be propagated to the children */
492
493addr_prop.address_data = deliver_address_data;
494addr_prop.domain_data = deliver_domain_data;
495addr_prop.localpart_data = deliver_localpart_data;
496addr_prop.errors_address = NULL;
497addr_prop.extra_headers = NULL;
498addr_prop.remove_headers = NULL;
499
500/* When verifying and testing addresses, the "logwrite" command in filters
501must be bypassed. */
502
503if (!verify && !address_test_mode) options |= RDO_REALLOG;
504
505/* Sort out the fixed or dynamic uid/gid. This uid is used (a) for reading the
506file (and interpreting a filter) and (b) for running the transports for
507generated file and pipe addresses. It is not (necessarily) the same as the uids
508that may own the file. Exim panics if an expanded string is not a number and
509can't be found in the password file. Other errors set the freezing bit. */
510
511if (!rf_get_ugid(rblock, addr, &ugid)) return DEFER;
512
513if (!ugid.uid_set && pw != NULL)
514 {
515 ugid.uid = pw->pw_uid;
516 ugid.uid_set = TRUE;
517 }
518
519if (!ugid.gid_set && pw != NULL)
520 {
521 ugid.gid = pw->pw_gid;
522 ugid.gid_set = TRUE;
523 }
524
525/* Call the function that interprets redirection data, either inline or from a
526file. This is a separate function so that the system filter can use it. It will
527run the function in a subprocess if necessary. If qualify_preserve_domain is
528set, temporarily reset qualify_domain_recipient to the current domain so that
529any unqualified addresses get qualified with the same domain as the incoming
530address. Otherwise, if a local qualify_domain is provided, set that up. */
531
532if (ob->qualify_preserve_domain)
533 qualify_domain_recipient = addr->domain;
534else if (ob->qualify_domain != NULL)
535 {
536 uschar *new_qdr = rf_expand_data(addr, ob->qualify_domain, &xrc);
537 if (new_qdr == NULL) return xrc;
538 qualify_domain_recipient = new_qdr;
539 }
540
541redirect.owners = ob->owners;
542redirect.owngroups = ob->owngroups;
543redirect.modemask = ob->modemask;
544redirect.check_owner = ob->check_owner;
545redirect.check_group = ob->check_group;
546redirect.pw = pw;
547
548if (ob->file != NULL)
549 {
550 redirect.string = ob->file;
551 redirect.isfile = TRUE;
552 }
553else
554 {
555 redirect.string = ob->data;
556 redirect.isfile = FALSE;
557 }
558
559frc = rda_interpret(&redirect, options, ob->include_directory,
560 ob->sieve_vacation_directory, &ugid, &generated, &(addr->message),
561 ob->skip_syntax_errors? &eblock : NULL, &filtertype,
562 string_sprintf("%s router (recipient is %s)", rblock->name, addr->address));
563
564qualify_domain_recipient = save_qualify_domain_recipient;
565
566/* Handle exceptional returns from filtering or processing an address list.
567For FAIL and FREEZE we honour any previously set up deliveries by a filter. */
568
569switch (frc)
570 {
571 case FF_NONEXIST:
572 addr->message = addr->user_message = NULL;
573 return DECLINE;
574
575 case FF_BLACKHOLE:
576 DEBUG(D_route) debug_printf("address :blackhole:d\n");
577 generated = NULL;
578 discarded = US":blackhole:";
579 frc = FF_DELIVERED;
580 break;
581
582 /* FF_DEFER and FF_FAIL can arise only as a result of explicit commands
583 (:fail: in an alias file or "fail" in a filter). If a configured message was
584 supplied, allow it to be included in an SMTP response after verifying. */
585
586 case FF_DEFER:
587 if (addr->message == NULL) addr->message = US"forced defer";
588 else addr->user_message = addr->message;
589 return DEFER;
590
591 case FF_FAIL:
592 if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) != OK)
593 return xrc;
594 add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw);
595 if (addr->message == NULL) addr->message = US"forced rejection";
596 else addr->user_message = addr->message;
597 return FAIL;
598
599 /* As in the case of a system filter, a freeze does not happen after a manual
600 thaw. In case deliveries were set up by the filter, we set the child count
601 high so that their completion does not mark the original address done. */
602
603 case FF_FREEZE:
604 if (!deliver_manual_thaw)
605 {
606 if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop))
607 != OK) return xrc;
608 add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw);
609 if (addr->message == NULL) addr->message = US"frozen by filter";
610 addr->special_action = SPECIAL_FREEZE;
611 addr->child_count = 9999;
612 return DEFER;
613 }
614 frc = FF_NOTDELIVERED;
615 break;
616
617 /* Handle syntax errors and :include: failures and lookup defers */
618
619 case FF_ERROR:
620 case FF_INCLUDEFAIL:
621
622 /* If filtertype is still FILTER_UNSET, it means that the redirection data
623 was never inspected, so the error was an expansion failure or failure to open
624 the file, or whatever. In these cases, the existing error message is probably
625 sufficient. */
626
627 if (filtertype == FILTER_UNSET) return DEFER;
628
629 /* If it was a filter and skip_syntax_errors is set, we want to set up
630 the error message so that it can be logged and mailed to somebody. */
631
632 if (filtertype != FILTER_FORWARD && ob->skip_syntax_errors)
633 {
634 eblock = store_get(sizeof(error_block));
635 eblock->next = NULL;
636 eblock->text1 = addr->message;
637 eblock->text2 = NULL;
638 addr->message = addr->user_message = NULL;
639 }
640
641 /* Otherwise set up the error for the address and defer. */
642
643 else
644 {
645 addr->basic_errno = ERRNO_BADREDIRECT;
646 addr->message = string_sprintf("error in %s %s: %s",
647 (filtertype != FILTER_FORWARD)? "filter" : "redirect",
648 (ob->data == NULL)? "file" : "data",
649 addr->message);
650 return DEFER;
651 }
652 }
653
654
655/* Yield is either FF_DELIVERED (significant action) or FF_NOTDELIVERED (no
656significant action). Before dealing with these, however, we must handle the
657effect of skip_syntax_errors.
658
659If skip_syntax_errors was set and there were syntax errors in an address list,
660error messages will be present in eblock. Log them and send a message if so
661configured. We cannot do this earlier, because the error message must not be
662sent as the local user. If there were no valid addresses, generated will be
663NULL. In this case, the router declines.
664
665For a filter file, the error message has been fudged into an eblock. After
666dealing with it, the router declines. */
667
668if (eblock != NULL)
669 {
670 if (!moan_skipped_syntax_errors(
671 rblock->name, /* For message content */
672 eblock, /* Ditto */
673 (verify || address_test_mode)?
674 NULL : ob->syntax_errors_to, /* Who to mail */
675 generated != NULL, /* True if not all failed */
676 ob->syntax_errors_text)) /* Custom message */
677 return DEFER;
678
679 if (filtertype != FILTER_FORWARD || generated == NULL)
680 {
681 addr->message = US"syntax error in redirection data";
682 return DECLINE;
683 }
684 }
685
686/* Sort out the errors address and any header modifications, and handle the
687generated addresses, if any. If there are no generated addresses, we must avoid
688calling sort_errors_and_headers() in case this router declines - that function
689may modify the errors_address field in the current address, and we don't want
690to do that for a decline. */
691
692if (generated != NULL)
693 {
694 if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) != OK)
695 return xrc;
696 add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw);
697 }
698
699/* FF_DELIVERED with no generated addresses is what we get when an address list
700contains :blackhole: or a filter contains "seen finish" without having
701generated anything. Log what happened to this address, and return DISCARD. */
702
703if (frc == FF_DELIVERED)
704 {
705 if (generated == NULL && !verify && !address_test_mode)
706 {
707 log_write(0, LOG_MAIN, "=> %s <%s> R=%s", discarded, addr->address,
708 rblock->name);
709 yield = DISCARD;
710 }
711 }
712
713/* For an address list, FF_NOTDELIVERED always means that no addresses were
714generated. For a filter, addresses may or may not have been generated. If none
715were, it's the same as an empty address list, and the router declines. However,
716if addresses were generated, we can't just decline because successful delivery
717of the base address gets it marked "done", so deferred generated addresses
718never get tried again. We have to generate a new version of the base address,
719as if there were a "deliver" command in the filter file, with the original
720address as parent. */
721
722else
723 {
724 address_item *next;
725
726 if (generated == NULL) return DECLINE;
727
728 next = deliver_make_addr(addr->address, FALSE);
729 next->parent = addr;
730 addr->child_count++;
731 next->next = *addr_new;
732 *addr_new = next;
733
734 /* Copy relevant flags (af_propagate is a name for the set), and set the
735 data that propagates. */
736
737 copyflag(next, addr, af_propagate);
738 next->p = addr_prop;
739
740 DEBUG(D_route) debug_printf("%s router autogenerated %s\n%s%s%s",
741 rblock->name,
742 next->address,
743 (addr_prop.errors_address != NULL)? " errors to " : "",
744 (addr_prop.errors_address != NULL)? addr_prop.errors_address : US"",
745 (addr_prop.errors_address != NULL)? "\n" : "");
746 }
747
748/* Control gets here only when the address has been completely handled. Put the
749original address onto the succeed queue so that any retry items that get
750attached to it get processed. */
751
752addr->next = *addr_succeed;
753*addr_succeed = addr;
754
755return yield;
756}
757
758/* End of routers/redirect.c */