-/* $Cambridge: exim/src/src/routers/redirect.c,v 1.7 2005/03/22 14:11:55 ph10 Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2017 */
/* 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) },
- #ifdef EXPAND_DLFUNC
{ "forbid_filter_dlfunc", opt_bit | (RDON_DLFUNC << 16),
(void *)offsetof(redirect_router_options_block, bit_options) },
- #endif
{ "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, 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, srs_alias) },
{ "srs_condition", opt_stringptr,
(void *)offsetof(redirect_router_options_block, srs_condition) },
- { "srs_db", opt_stringptr,
- (void *)offsetof(redirect_router_options_block, srs_db) },
+ { "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) },
int redirect_router_options_count =
sizeof(redirect_router_options)/sizeof(optionlist);
+
+#ifdef MACRO_PREDEF
+
+/* Dummy entries */
+redirect_router_options_block redirect_router_option_defaults = {0};
+void redirect_router_init(router_instance *rblock) {}
+int redirect_router_entry(router_instance *rblock, address_item *addr,
+ struct passwd *pw, int verify, address_item **addr_local,
+ address_item **addr_remote, address_item **addr_new,
+ address_item **addr_succeed) {return 0;}
+
+#else /*!MACRO_PREDEF*/
+
+
+
/* Default private options block for the redirect router. */
redirect_router_options_block redirect_router_option_defaults = {
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, /* owngroups */
#ifdef EXPERIMENTAL_SRS
NULL, /* srs */
- NULL, /* srs_condition */
- NULL, /* srs_db */
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 */
if (ob->one_time)
{
ob->forbid_pipe = ob->forbid_file = ob->forbid_filter_reply = TRUE;
- if (rblock->extra_headers != NULL || rblock->remove_headers != NULL)
+ if (rblock->extra_headers || rblock->remove_headers)
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)
+ if (rblock->unseen || rblock->expand_unseen)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
"\"unseen\" may not be used with \"one_time\"", rblock->name);
}
if (ob->check_owner == TRUE_UNSET)
ob->check_owner = rblock->check_local_user ||
- (ob->owners != NULL && ob->owners[0] != 0);
+ (ob->owners && ob->owners[0] != 0);
if (ob->check_group == TRUE_UNSET)
ob->check_group = (rblock->check_local_user && (ob->modemask & 020) == 0) ||
/* If explicit qualify domain set, the preserve option is locked out */
-if (ob->qualify_domain != NULL && ob->qualify_preserve_domain)
+if (ob->qualify_domain && ob->qualify_preserve_domain)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
"only one of \"qualify_domain\" or \"qualify_preserve_domain\" must be set",
rblock->name);
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));
+ &addr_prop->errors_address);
if (frc != OK) return frc;
-addr->p.errors_address = addr_prop->errors_address;
-return rf_get_munge_headers(addr, rblock, &(addr_prop->extra_headers),
- &(addr_prop->remove_headers));
+addr->prop.errors_address = addr_prop->errors_address;
+return rf_get_munge_headers(addr, rblock, &addr_prop->extra_headers,
+ &addr_prop->remove_headers);
}
redirect_router_options_block *ob =
(redirect_router_options_block *)(rblock->options_block);
-while (generated != NULL)
+while (generated)
{
address_item *parent;
address_item *next = generated;
- uschar *errors_address = next->p.errors_address;
+ uschar *errors_address = next->prop.errors_address;
generated = next->next;
next->parent = addr;
- orflag(next, addr, af_ignore_error);
next->start_router = rblock->redirect_router;
+ if (addr->child_count == USHRT_MAX)
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router generated more than %d "
+ "child addresses for <%s>", rblock->name, USHRT_MAX, addr->address);
addr->child_count++;
next->next = *addr_new;
if (ob->one_time && !queue_2stage)
{
- for (parent = addr; parent->parent != NULL; parent = parent->parent);
+ for (parent = addr; parent->parent; parent = parent->parent) ;
next->onetime_parent = parent->address;
}
unless the ancestor was routed by a case-sensitive router. */
if (ob->check_ancestor)
- {
- for (parent = addr; parent != NULL; parent = parent->parent)
- {
- if (((parent->router != NULL && parent->router->caseful_local_part)?
- Ustrcmp(next->address, parent->address)
- :
- strcmpic(next->address, parent->address)
+ for (parent = addr; parent; parent = parent->parent)
+ if ((parent->router && parent->router->caseful_local_part
+ ? Ustrcmp(next->address, parent->address)
+ : strcmpic(next->address, parent->address)
) == 0)
{
DEBUG(D_route) debug_printf("generated parent replaced by child\n");
next->address = string_copy(addr->address);
break;
}
- }
- }
/* A user filter may, under some circumstances, set up an errors address.
If so, we must take care to re-instate it when we copy in the propagated
data so that it overrides any errors_to setting on the router. */
- next->p = *addr_prop;
- if (errors_address != NULL) next->p.errors_address = errors_address;
+ {
+ BOOL ignore_error = next->prop.ignore_error;
+ next->prop = *addr_prop;
+ next->prop.ignore_error = ignore_error || addr->prop.ignore_error;
+ }
+ if (errors_address) next->prop.errors_address = errors_address;
/* For pipes, files, and autoreplies, record this router as handling them,
because they don't go through the routing process again. Then set up uid,
}
}
+#ifdef SUPPORT_I18N
+ next->prop.utf8_msg = string_is_utf8(next->address)
+ || (sender_address && string_is_utf8(sender_address));
+#endif
+
DEBUG(D_route)
{
debug_printf("%s router generated %s\n %serrors_to=%s transport=%s\n",
rblock->name,
next->address,
testflag(next, af_pfr)? "pipe, file, or autoreply\n " : "",
- next->p.errors_address,
+ next->prop.errors_address,
(next->transport == NULL)? US"NULL" : next->transport->name);
if (testflag(next, af_uid_set))
else
debug_printf("gid=unset ");
+#ifdef SUPPORT_I18N
+ if (next->prop.utf8_msg) debug_printf("utf8 ");
+#endif
+
debug_printf("home=%s\n", next->home_dir);
}
}
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 */
redirect_router_options_block *ob =
(redirect_router_options_block *)(rblock->options_block);
address_item *generated = NULL;
-uschar *save_qualify_domain_recipient = qualify_domain_recipient;
+const uschar *save_qualify_domain_recipient = qualify_domain_recipient;
uschar *discarded = US"discarded";
address_item_propagated addr_prop;
error_block *eblock = NULL;
addr_prop.extra_headers = NULL;
addr_prop.remove_headers = NULL;
+#ifdef EXPERIMENTAL_SRS
+addr_prop.srs_sender = NULL;
+#endif
+#ifdef SUPPORT_I18N
+addr_prop.utf8_msg = FALSE; /*XXX should we not copy this from the parent? */
+addr_prop.utf8_downcvt = FALSE;
+addr_prop.utf8_downcvt_maybe = FALSE;
+#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
}
#ifdef EXPERIMENTAL_SRS
- /* For reverse SRS, fill the srs_recipient expandsion variable,
- on failure, return decline/fail as relevant */
+ /* Perform SRS on recipient/return-path as required */
+
if(ob->srs != NULL)
{
BOOL usesrs = TRUE;
usesrs = expand_check_condition(ob->srs_condition, "srs_condition expansion failed", NULL);
if(usesrs)
- if(Ustrcmp(ob->srs, "reverse") == 0 || Ustrcmp(ob->srs, "reverseandforward") == 0)
+ {
+ 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)
{
- uschar *res;
- int n_srs;
+ 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_db)
- eximsrs_db_set(TRUE, ob->srs_db);
- if((n_srs = eximsrs_reverse(&res, addr->address)) != OK)
+ 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;
- srs_recipient = res;
+ }
+
+ /* 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 (!(usedomain = ob->srs_alias ? expand_string(ob->srs_alias) : NULL))
+ usedomain = string_copy(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();
- DEBUG(D_any)
- debug_printf("SRS: Recipient '%s' rewritten to '%s'\n", srs_orig_recipient, srs_recipient);
+
+ if(n_srs != OK)
+ return n_srs;
}
+ }
}
#endif
}
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);
next->next = *addr_new;
*addr_new = next;
- /* Copy relevant flags (af_propagate is a name for the set), and set the
- data that propagates. */
+ /* Set the data that propagates. */
- copyflag(next, addr, af_propagate);
- next->p = addr_prop;
+ next->prop = addr_prop;
DEBUG(D_route) debug_printf("%s router autogenerated %s\n%s%s%s",
rblock->name,
(addr_prop.errors_address != NULL)? "\n" : "");
}
-#ifdef EXPERIMENTAL_SRS
- /* On successful redirection, check for SRS forwarding and adjust sender */
- 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)
- if((Ustrcmp(ob->srs, "forward") == 0 || Ustrcmp(ob->srs, "reverseandforward") == 0) && !verify)
- {
- uschar *res;
- uschar *usedomain;
- int n_srs;
-
- srs_orig_sender = sender_address;
- eximsrs_init();
- if(ob->srs_db)
- eximsrs_db_set(FALSE, ob->srs_db);
-
- 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)
- return n_srs;
- sender_address = res;
- DEBUG(D_any)
- debug_printf("SRS: Sender '%s' rewritten to '%s'\n", srs_orig_sender, sender_address);
- }
- }
-#endif
-
/* Control gets here only when the address has been completely handled. Put the
original address onto the succeed queue so that any retry items that get
attached to it get processed. */
return yield;
}
+#endif /*!MACRO_PREDEF*/
/* End of routers/redirect.c */