-/* $Cambridge: exim/src/src/routers/redirect.c,v 1.1 2004/10/07 13:10:02 ph10 Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
/* See the file NOTICE for conditions of use and distribution. */
(void *)offsetof(redirect_router_options_block, file) },
{ "file_transport", opt_stringptr,
(void *)offsetof(redirect_router_options_block, file_transport_name) },
+ { "filter_prepend_home",opt_bit | (RDON_PREPEND_HOME << 16),
+ (void *)offsetof(redirect_router_options_block, bit_options) },
{ "forbid_blackhole", opt_bit | (RDON_BLACKHOLE << 16),
(void *)offsetof(redirect_router_options_block, bit_options) },
+ { "forbid_exim_filter", opt_bit | (RDON_EXIM_FILTER << 16),
+ (void *)offsetof(redirect_router_options_block, bit_options) },
{ "forbid_file", opt_bool,
(void *)offsetof(redirect_router_options_block, forbid_file) },
+ { "forbid_filter_dlfunc", opt_bit | (RDON_DLFUNC << 16),
+ (void *)offsetof(redirect_router_options_block, bit_options) },
{ "forbid_filter_existstest", opt_bit | (RDON_EXISTS << 16),
(void *)offsetof(redirect_router_options_block, bit_options) },
{ "forbid_filter_logwrite",opt_bit | (RDON_LOG << 16),
(void *)offsetof(redirect_router_options_block, bit_options) },
{ "forbid_filter_lookup", opt_bit | (RDON_LOOKUP << 16),
(void *)offsetof(redirect_router_options_block, bit_options) },
- #ifdef EXIM_PERL
{ "forbid_filter_perl", opt_bit | (RDON_PERL << 16),
(void *)offsetof(redirect_router_options_block, bit_options) },
- #endif
{ "forbid_filter_readfile", opt_bit | (RDON_READFILE << 16),
(void *)offsetof(redirect_router_options_block, bit_options) },
{ "forbid_filter_readsocket", opt_bit | (RDON_READSOCK << 16),
(void *)offsetof(redirect_router_options_block, bit_options) },
{ "forbid_pipe", opt_bool,
(void *)offsetof(redirect_router_options_block, forbid_pipe) },
+ { "forbid_sieve_filter",opt_bit | (RDON_SIEVE_FILTER << 16),
+ (void *)offsetof(redirect_router_options_block, bit_options) },
+ { "forbid_smtp_code", opt_bool,
+ (void *)offsetof(redirect_router_options_block, forbid_smtp_code) },
{ "hide_child_in_errmsg", opt_bool,
(void *)offsetof(redirect_router_options_block, hide_child_in_errmsg) },
{ "ignore_eacces", opt_bit | (RDON_EACCES << 16),
(void *)offsetof(redirect_router_options_block, reply_transport_name) },
{ "rewrite", opt_bit | (RDON_REWRITE << 16),
(void *)offsetof(redirect_router_options_block, bit_options) },
+ { "sieve_enotify_mailto_owner", opt_stringptr,
+ (void *)offsetof(redirect_router_options_block, sieve_enotify_mailto_owner) },
+ { "sieve_subaddress", opt_stringptr,
+ (void *)offsetof(redirect_router_options_block, sieve_subaddress) },
+ { "sieve_useraddress", opt_stringptr,
+ (void *)offsetof(redirect_router_options_block, sieve_useraddress) },
{ "sieve_vacation_directory", opt_stringptr,
(void *)offsetof(redirect_router_options_block, sieve_vacation_directory) },
{ "skip_syntax_errors", opt_bool,
(void *)offsetof(redirect_router_options_block, skip_syntax_errors) },
+#ifdef EXPERIMENTAL_SRS
+ { "srs", opt_stringptr,
+ (void *)offsetof(redirect_router_options_block, srs) },
+ { "srs_alias", opt_stringptr,
+ (void *)offsetof(redirect_router_options_block, srs_alias) },
+ { "srs_condition", opt_stringptr,
+ (void *)offsetof(redirect_router_options_block, srs_condition) },
+ { "srs_dbinsert", opt_stringptr,
+ (void *)offsetof(redirect_router_options_block, srs_dbinsert) },
+ { "srs_dbselect", opt_stringptr,
+ (void *)offsetof(redirect_router_options_block, srs_dbselect) },
+#endif
{ "syntax_errors_text", opt_stringptr,
(void *)offsetof(redirect_router_options_block, syntax_errors_text) },
{ "syntax_errors_to", opt_stringptr,
NULL, /* include_directory */
NULL, /* pipe_transport_name */
NULL, /* reply_transport_name */
+ NULL, /* sieve_subaddress */
+ NULL, /* sieve_useraddress */
NULL, /* sieve_vacation_directory */
+ NULL, /* sieve_enotify_mailto_owner */
NULL, /* syntax_errors_text */
NULL, /* syntax_errors_to */
NULL, /* qualify_domain */
NULL, /* owners */
NULL, /* owngroups */
+#ifdef EXPERIMENTAL_SRS
+ NULL, /* srs */
+ NULL, /* srs_alias */
+ NULL, /* srs_condition */
+ NULL, /* srs_dbinsert */
+ NULL, /* srs_dbselect */
+#endif
022, /* modemask */
- RDO_REWRITE, /* bit_options */
+ RDO_REWRITE | RDO_PREPEND_HOME, /* bit_options */
FALSE, /* check_ancestor */
TRUE_UNSET, /* check_owner */
TRUE_UNSET, /* check_group */
FALSE, /* forbid_file */
FALSE, /* forbid_filter_reply */
FALSE, /* forbid_pipe */
+ FALSE, /* forbid_smtp_code */
FALSE, /* hide_child_in_errmsg */
FALSE, /* one_time */
FALSE, /* qualify_preserve_domain */
"%sone of \"file\" or \"data\" must be specified",
rblock->name, (ob->file == NULL)? "" : "only ");
-/* Onetime aliases can only be real addresses. Headers can't be manipulated. */
+/* Onetime aliases can only be real addresses. Headers can't be manipulated.
+The combination of one_time and unseen is not allowed. We can't check the
+expansion of "unseen" here, but we assume that if it is set to anything other
+than false, there is likely to be a problem. */
if (ob->one_time)
{
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
"\"headers_add\" and \"headers_remove\" are not permitted with "
"\"one_time\"", rblock->name);
+ if (rblock->unseen || rblock->expand_unseen != NULL)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
+ "\"unseen\" may not be used with \"one_time\"", rblock->name);
}
/* The defaults for check_owner and check_group depend on other settings. The
Arguments:
rblock the router control block
addr the address being routed
- verify true if verifying
+ verify v_none/v_recipient/v_sender/v_expn
addr_prop point to the propagated block, which is where the
new values are to be placed
static int
sort_errors_and_headers(router_instance *rblock, address_item *addr,
- BOOL verify, address_item_propagated *addr_prop)
+ int verify, address_item_propagated *addr_prop)
{
int frc = rf_get_errors_address(addr, rblock, verify,
&(addr_prop->errors_address));
next->parent = addr;
orflag(next, addr, af_ignore_error);
next->start_router = rblock->redirect_router;
+ if (addr->child_count == SHRT_MAX)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router generated more than %d "
+ "child addresses for <%s>", rblock->name, SHRT_MAX, addr->address);
addr->child_count++;
next->next = *addr_new;
router_instance *rblock, /* data for this instantiation */
address_item *addr, /* address we are working on */
struct passwd *pw, /* passwd entry after check_local_user */
- BOOL verify, /* TRUE when verifying */
+ int verify, /* v_none/v_recipient/v_sender/v_expn */
address_item **addr_local, /* add it to this if it's local */
address_item **addr_remote, /* add it to this if it's remote */
address_item **addr_new, /* put new addresses on here */
addr_prop.extra_headers = NULL;
addr_prop.remove_headers = NULL;
+#ifdef EXPERIMENTAL_SRS
+addr_prop.srs_sender = NULL;
+#endif
+
/* When verifying and testing addresses, the "logwrite" command in filters
must be bypassed. */
-if (!verify && !address_test_mode) options |= RDO_REALLOG;
+if (verify == v_none && !address_test_mode) options |= RDO_REALLOG;
/* Sort out the fixed or dynamic uid/gid. This uid is used (a) for reading the
file (and interpreting a filter) and (b) for running the transports for
ugid.gid_set = TRUE;
}
+#ifdef EXPERIMENTAL_SRS
+ /* Perform SRS on recipient/return-path as required */
+
+ if(ob->srs != NULL)
+ {
+ BOOL usesrs = TRUE;
+
+ if(ob->srs_condition != NULL)
+ usesrs = expand_check_condition(ob->srs_condition, "srs_condition expansion failed", NULL);
+
+ if(usesrs)
+ {
+ int srs_action = 0, n_srs;
+ uschar *res;
+ uschar *usedomain;
+
+ /* What are we doing? */
+ if(Ustrcmp(ob->srs, "forward") == 0)
+ srs_action = 1;
+ else if(Ustrcmp(ob->srs, "reverseandforward") == 0)
+ {
+ srs_action = 3;
+
+ if((ob->srs_dbinsert == NULL) ^ (ob->srs_dbselect == NULL))
+ return DEFER;
+ }
+ else if(Ustrcmp(ob->srs, "reverse") == 0)
+ srs_action = 2;
+
+ /* Reverse SRS */
+ if(srs_action & 2)
+ {
+ srs_orig_recipient = addr->address;
+
+ eximsrs_init();
+ if(ob->srs_dbselect)
+ eximsrs_db_set(TRUE, ob->srs_dbselect);
+/* Comment this out for now...
+// else
+// eximsrs_db_set(TRUE, NULL);
+*/
+
+ if((n_srs = eximsrs_reverse(&res, addr->address)) == OK)
+ {
+ srs_recipient = res;
+ DEBUG(D_any)
+ debug_printf("SRS (reverse): Recipient '%s' rewritten to '%s'\n", srs_orig_recipient, srs_recipient);
+ }
+
+ eximsrs_done();
+
+ if(n_srs != OK)
+ return n_srs;
+ }
+
+ /* Forward SRS */
+ /* No point in actually performing SRS if we are just verifying a recipient */
+ if((srs_action & 1) && verify == v_none &&
+ (sender_address ? sender_address[0] != 0 : FALSE))
+ {
+
+ srs_orig_sender = sender_address;
+ eximsrs_init();
+ if(ob->srs_dbinsert)
+ eximsrs_db_set(FALSE, ob->srs_dbinsert);
+/* Comment this out for now...
+// else
+// eximsrs_db_set(FALSE, NULL);
+*/
+
+ if(ob->srs_alias != NULL ? (usedomain = expand_string(ob->srs_alias)) == NULL : 1)
+ usedomain = deliver_domain;
+
+ if((n_srs = eximsrs_forward(&res, sender_address, usedomain)) == OK)
+ {
+ addr_prop.srs_sender = res;
+ DEBUG(D_any)
+ debug_printf("SRS (forward): Sender '%s' rewritten to '%s'\n", srs_orig_sender, res);
+ }
+
+ eximsrs_done();
+
+ if(n_srs != OK)
+ return n_srs;
+ }
+ }
+ }
+#endif
+
/* Call the function that interprets redirection data, either inline or from a
file. This is a separate function so that the system filter can use it. It will
run the function in a subprocess if necessary. If qualify_preserve_domain is
}
frc = rda_interpret(&redirect, options, ob->include_directory,
- ob->sieve_vacation_directory, &ugid, &generated, &(addr->message),
- ob->skip_syntax_errors? &eblock : NULL, &filtertype,
+ ob->sieve_vacation_directory, ob->sieve_enotify_mailto_owner,
+ ob->sieve_useraddress, ob->sieve_subaddress, &ugid, &generated,
+ &(addr->message), ob->skip_syntax_errors? &eblock : NULL, &filtertype,
string_sprintf("%s router (recipient is %s)", rblock->name, addr->address));
qualify_domain_recipient = save_qualify_domain_recipient;
break;
/* FF_DEFER and FF_FAIL can arise only as a result of explicit commands
- (:fail: in an alias file or "fail" in a filter). If a configured message was
- supplied, allow it to be included in an SMTP response after verifying. */
+ (:defer: or :fail: in an alias file or "fail" in a filter). If a configured
+ message was supplied, allow it to be included in an SMTP response after
+ verifying. Remove any SMTP code if it is not allowed. */
case FF_DEFER:
- if (addr->message == NULL) addr->message = US"forced defer";
- else addr->user_message = addr->message;
- return DEFER;
+ yield = DEFER;
+ goto SORT_MESSAGE;
case FF_FAIL:
if ((xrc = sort_errors_and_headers(rblock, addr, verify, &addr_prop)) != OK)
return xrc;
add_generated(rblock, addr_new, addr, generated, &addr_prop, &ugid, pw);
- if (addr->message == NULL) addr->message = US"forced rejection";
- else addr->user_message = addr->message;
- return FAIL;
+ yield = FAIL;
+
+ SORT_MESSAGE:
+ if (addr->message == NULL)
+ addr->message = (yield == FAIL)? US"forced rejection" : US"forced defer";
+ else
+ {
+ int ovector[3];
+ if (ob->forbid_smtp_code &&
+ pcre_exec(regex_smtp_code, NULL, CS addr->message,
+ Ustrlen(addr->message), 0, PCRE_EOPT,
+ ovector, sizeof(ovector)/sizeof(int)) >= 0)
+ {
+ DEBUG(D_route) debug_printf("SMTP code at start of error message "
+ "is ignored because forbid_smtp_code is set\n");
+ addr->message += ovector[1];
+ }
+ addr->user_message = addr->message;
+ setflag(addr, af_pass_message);
+ }
+ return yield;
/* As in the case of a system filter, a freeze does not happen after a manual
thaw. In case deliveries were set up by the filter, we set the child count
if (eblock != NULL)
{
if (!moan_skipped_syntax_errors(
- rblock->name, /* For message content */
- eblock, /* Ditto */
- (verify || address_test_mode)?
- NULL : ob->syntax_errors_to, /* Who to mail */
- generated != NULL, /* True if not all failed */
- ob->syntax_errors_text)) /* Custom message */
+ rblock->name, /* For message content */
+ eblock, /* Ditto */
+ (verify != v_none || address_test_mode)?
+ NULL : ob->syntax_errors_to, /* Who to mail */
+ generated != NULL, /* True if not all failed */
+ ob->syntax_errors_text)) /* Custom message */
return DEFER;
if (filtertype != FILTER_FORWARD || generated == NULL)
if (frc == FF_DELIVERED)
{
- if (generated == NULL && !verify && !address_test_mode)
+ if (generated == NULL && verify == v_none && !address_test_mode)
{
log_write(0, LOG_MAIN, "=> %s <%s> R=%s", discarded, addr->address,
rblock->name);