.section "Logging" "SECID99"
.table2
+.row &%event_action%& "custom logging"
.row &%hosts_connection_nolog%& "exemption from connect logging"
.row &%log_file_path%& "override compiled-in value"
.row &%log_selector%& "set/unset optional logging"
not used.
+.new
+.option event_action main string&!! unset
+.cindex events
+This option declares a string to be expanded for Exim's events mechanism.
+For details see &<<CHAPevents>>&.
+.wen
+
+
.option exim_group main string "compile-time configured"
.cindex "gid (group id)" "Exim's own"
.cindex "Exim group"
resent to other recipients.
+.option event_action transports string&!! unset
+.cindex events
+This option declares a string to be expanded for Exim's events mechanism.
+For details see &<<CHAPevents>>&.
+.wen
+
+
.option group transports string&!! "Exim group"
.cindex "transport" "group; specifying"
This option specifies a gid for running the transport process, overriding any
DKIM is documented in RFC 4871.
Since version 4.70, DKIM support is compiled into Exim by default. It can be
-disabled by setting DISABLE_DKIM=yes in Local/Makefile.
+disabled by setting DISABLE_DKIM=yes in &_Local/Makefile_&.
Exim's DKIM implementation allows to
.olist
. ////////////////////////////////////////////////////////////////////////////
. ////////////////////////////////////////////////////////////////////////////
+.chapter "Events" "CHAPevents" &&&
+ "Events"
+.cindex events
+
+.new
+The events mechanism in Exim can be used to intercept processing at a number
+of points. It was originally invented to giave a way to do customised logging
+actions (for example, to a database) but can also be used to modify some
+processing actions.
+
+Most installations will never need to use Events.
+The support can be left out of a build by defining DISABLE_EVENT=yes
+in &_Local/Makefile_&.
+
+There are two major classes of events: main and transport.
+The main configuration option &%event_action%& controls reception events;
+a transport option &%event_action%& controls delivery events.
+
+Both options are a string which is expanded when the event fires.
+An example might look like:
+.cindex logging custom
+.code
+event_action = ${if eq {msg:delivery}{$event_name} \
+{${lookup pgsql {SELECT * FROM record_Delivery( \
+ '${quote_pgsql:$sender_address_domain}',\
+ '${quote_pgsql:${lc:$sender_address_local_part}}', \
+ '${quote_pgsql:$domain}', \
+ '${quote_pgsql:${lc:$local_part}}', \
+ '${quote_pgsql:$host_address}', \
+ '${quote_pgsql:${lc:$host}}', \
+ '${quote_pgsql:$message_exim_id}')}} \
+} {}}
+.endd
+
+Events have names which correspond to the point in process at which they fire.
+The name is placed in the variable &$event_name$& and the event action
+expansion must check this, as it will be called for every possible event type.
+
+The current list of events is:
+.display
+&`msg:complete after main `& per message
+&`msg:delivery after transport `& per recipient
+&`msg:rcpt:host:defer after transport `& per recipient per host
+&`msg:rcpt:defer after transport `& per recipient
+&`msg:host:defer after transport `& per attempt
+&`msg:fail:delivery after main `& per recipient
+&`msg:fail:internal after main `& per recipient
+&`tcp:connect before transport `& per connection
+&`tcp:close after transport `& per connection
+&`tls:cert before both `& per certificate in verification chain
+&`smtp:connect after transport `& per connection
+.endd
+New event types may be added in future.
+
+The event name is a colon-separated list, defining the type of
+event in a tree of possibilities. It may be used as a list
+or just matched on as a whole. There will be no spaces in the name.
+
+The second column in the table above describes whether the event fires
+before or after the action is associates with. Those which fire before
+can be used to affect that action (more on this below).
+
+An additional variable, &$event_data$&, is filled with information varying
+with the event type:
+.display
+&`msg:delivery `& smtp confirmation mssage
+&`msg:rcpt:host:defer `& error string
+&`msg:rcpt:defer `& error string
+&`msg:host:defer `& error string
+&`tls:cert `& verification chain depth
+&`smtp:connect `& smtp banner
+.endd
+
+The :defer events populate one extra variable: &$event_defer_errno$&.
+
+For complex operations an ACL expansion can be used in &%event_action%&
+however due to the multiple contextx that Exim operates in during
+the course of its processing:
+.ilist
+variables set in transport events will not be visible outside that
+transport call
+.next
+acl_m variables in a server context are lost on a new connection,
+and after smtp helo/ehlo/mail/starttls/rset commands
+.endlist
+Using an ACL expansion with the logwrite modifier can be
+a useful way of writing to the main log.
+
+The expansion of the event_action option should normally
+return an empty string. Should it return anything else the
+following will be forced:
+.display
+&`msg:delivery `& (ignored)
+&`msg:host:defer `& (ignored)
+&`msg:fail:delivery`& (ignored)
+&`tcp:connect `& do not connect
+&`tcp:close `& (ignored)
+&`tls:cert `& refuse verification
+&`smtp:connect `& close connection
+.endd
+No other use is made of the result string.
+
+For a tcp:connect event, if the connection is being made to a proxy
+then the address and port variables will be that of the proxy and not
+the target system.
+
+For tls:cert events, if GnuTLS is in use this will trigger only per
+chain element received on the connection.
+For OpenSSL it will trigger for every chain element including those
+loaded locally.
+.wen
+
+. ////////////////////////////////////////////////////////////////////////////
+. ////////////////////////////////////////////////////////////////////////////
+
.chapter "Adding new drivers or lookup types" "CHID13" &&&
"Adding drivers or lookups"
.cindex "adding drivers"
JH/28 Bug 1745: Fix redis lookups to handle (quoted) spaces embedded in parts
of the query string, and make ${quote_redis:} do that quoting.
+JH/29 Move Events support from Experimental to mainline, enabled by default
+ and removable for a build by defining DISABLE_EVENT.
+
Exim version 4.86
-----------------
-Event Actions
---------------------------------------------------------------
-
-(Renamed from TPDA, Transport post-delivery actions)
-
-An arbitrary per-transport string can be expanded upon various transport events.
-Additionally a main-section configuration option can be expanded on some
-per-message events.
-This feature may be used, for example, to write exim internal log information
-(not available otherwise) into a database.
-
-In order to use the feature, you must compile with
-
-EXPERIMENTAL_EVENT=yes
-
-in your Local/Makefile
-
-and define one or both of
-- the event_action option in the transport
-- the event_action main option
-to be expanded when the event fires.
-
-A new variable, $event_name, is set to the event type when the
-expansion is done. The current list of events is:
-
- msg:complete after main per message
- msg:delivery after transport per recipient
- msg:rcpt:host:defer after transport per recipient per host
- msg:rcpt:defer after transport per recipient
- msg:host:defer after transport per attempt
- msg:fail:delivery after main per recipient
- msg:fail:internal after main per recipient
- tcp:connect before transport per connection
- tcp:close after transport per connection
- tls:cert before both per certificate in verification chain
- smtp:connect after transport per connection
-
-The expansion is called for all event types, and should use the $event_name
-variable to decide when to act. The value of the variable is a colon-separated
-list, defining a position in the tree of possible events; it may be used as
-a list or just matched on as a whole. There will be no whitespace.
-
-New event types may be added in the future.
-
-
-There is an auxilary variable, $event_data, for which the
-content is event_dependent:
-
- msg:delivery smtp confirmation mssage
- msg:rcpt:host:defer error string
- msg:rcpt:defer error string
- msg:host:defer error string
- tls:cert verification chain depth
- smtp:connect smtp banner
-
-The :defer events populate one extra variable, $event_defer_errno.
-
-The following variables are likely to be useful depending on the event type:
-
- router_name, transport_name
- local_part, domain
- host, host_address, host_port
- tls_out_peercert
- lookup_dnssec_authenticated, tls_out_dane
- sending_ip_address, sending_port
- message_exim_id, verify_mode
-
-
-An example might look like:
-
-event_action = ${if eq {msg:delivery}{$event_name} \
-{${lookup pgsql {SELECT * FROM record_Delivery( \
- '${quote_pgsql:$sender_address_domain}',\
- '${quote_pgsql:${lc:$sender_address_local_part}}', \
- '${quote_pgsql:$domain}', \
- '${quote_pgsql:${lc:$local_part}}', \
- '${quote_pgsql:$host_address}', \
- '${quote_pgsql:${lc:$host}}', \
- '${quote_pgsql:$message_exim_id}')}} \
-} {}}
-
-The string is expanded when each of the supported events occur
-and any side-effects of the expansion will happen.
-
-Note that for complex operations an ACL expansion can be used,
-however due to the multiple contexts the Exim operates in
-a) variables set in events raised from transports will not
- be visible outside that transport call.
-b) acl_m variables in a server context are lost on a new connection,
- and after helo/ehlo/mail/starttls/rset commands
-Using an ACL expansion with the logwrite modifier can be a
-useful way of writing to the main log.
-
-
-
-The expansion of the event_action option should normally
-return an empty string. Should it return anything else the
-following will be forced:
-
- msg:delivery (ignored)
- msg:host:defer (ignored)
- msg:fail:delivery (ignored)
- tcp:connect do not connect
- tcp:close (ignored)
- tls:cert refuse verification
- smtp:connect close connection
-
-No other use is made of the result string.
-
-If transport proxying is used, the remote IP/port during a
-tcp:connect event will be that of the proxy.
-
-
-Known issues:
-- the tls:cert event is only called for the cert chain elements
- received over the wire, with GnuTLS. OpenSSL gives the entire
- chain including those loaded locally.
-
-
Redis Lookup
--------------------------------------------------------------
# DISABLE_DNSSEC=yes
+# To disable support for Events set DISABLE_EVENT to "yes"
+
+# DISABLE_EVENT=yes
+
#------------------------------------------------------------------------------
# Compiling Exim with experimental features. These are documented in
# LDFLAGS += -lopendmarc
-# Uncomment the following line to support Events,
-# eg. for logging to a database.
-# EXPERIMENTAL_EVENT=yes
-
# Uncomment the following line to add Redis lookup support
# You need to have hiredis installed on your system (https://github.com/redis/hiredis).
# Depending on where it is installed you may have to edit the CFLAGS and LDFLAGS lines.
{
int old_pool = store_pool;
if ( cb->u.varname[0] == 'c'
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
|| event_name /* An event is being delivered */
#endif
)
#define DELIVER_OUT_BUFFER_SIZE 8192
#define DISABLE_DNSSEC
#define DISABLE_DKIM
+#define DISABLE_EVENT
#define DISABLE_PRDR
#define DISABLE_OCSP
#define DISABLE_D_OPTION
#define EXPERIMENTAL_DCC
#define EXPERIMENTAL_DSN_INFO
#define EXPERIMENTAL_DMARC
-#define EXPERIMENTAL_EVENT
#define EXPERIMENTAL_REDIS
#define EXPERIMENTAL_SPF
#define EXPERIMENTAL_SRS
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
uschar *
event_raise(uschar * action, const uschar * event, uschar * ev_data)
{
deliver_domain = save_domain;
router_name = transport_name = NULL;
}
-#endif /*EXPERIMENTAL_EVENT*/
+#endif /*DISABLE_EVENT*/
have a pointer to the host item that succeeded; local deliveries can have a
pointer to a single host item in their host list, for use by the transport. */
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
/* presume no successful remote delivery */
lookup_dnssec_authenticated = NULL;
#endif
if (continue_sequence > 1)
s = string_cat(s, &size, &ptr, US"*", 1);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
deliver_host_address = addr->host_used->address;
deliver_host_port = addr->host_used->port;
deliver_host = addr->host_used->name;
s[ptr] = 0;
log_write(0, flags, "%s", s);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
if (!msg) msg_event_raise(US"msg:delivery", addr);
#endif
log_write(0, LOG_MAIN, "** %s", s);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
msg_event_raise(US"msg:fail:delivery", addr);
#endif
break;
}
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
if (process_recipients != RECIP_ACCEPT)
{
uschar * save_local = deliver_localpart;
/* Unset deliver_freeze so that we won't try to move the spool files further down */
deliver_freeze = FALSE;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
(void) event_raise(event_action, US"msg:complete", NULL);
#endif
}
#ifndef DISABLE_DNSSEC
fprintf(f, " DNSSEC");
#endif
+#ifndef DISABLE_EVENT
+ fprintf(f, " Event");
+#endif
#ifdef SUPPORT_I18N
fprintf(f, " I18N");
#endif
#ifdef EXPERIMENTAL_DSN_INFO
fprintf(f, " Experimental_DSN_info");
#endif
-#ifdef EXPERIMENTAL_EVENT
- fprintf(f, " Experimental_Event");
-#endif
#ifdef EXPERIMENTAL_REDIS
fprintf(f, " Experimental_Redis");
#endif
{ "dnslist_value", vtype_stringptr, &dnslist_value },
{ "domain", vtype_stringptr, &deliver_domain },
{ "domain_data", vtype_stringptr, &deliver_domain_data },
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
{ "event_data", vtype_stringptr, &event_data },
/*XXX want to use generic vars for as many of these as possible*/
extern void enq_end(uschar *);
extern BOOL enq_start(uschar *, unsigned);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
extern uschar *event_raise(uschar *, const uschar *, uschar *);
extern void msg_event_raise(const uschar *, const address_item *);
#endif
int error_handling = ERRORS_SENDER;
uschar *errors_reply_to = NULL;
int errors_sender_rc = EXIT_FAILURE;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
uschar *event_action = NULL; /* expansion for delivery events */
uschar *event_data = NULL; /* auxilary data variable for event */
int event_defer_errno = 0;
FALSE, /* log_defer_output */
TRUE_UNSET /* retry_use_local_part: BOOL, but set neither
1 nor 0 so can detect unset */
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
,NULL /* event_action */
#endif
};
extern uschar *errors_reply_to; /* Reply-to for error messages */
extern int errors_sender_rc; /* Return after message to sender*/
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
extern uschar *event_action; /* expansion for delivery events */
extern uschar *event_data; /* event data */
extern int event_defer_errno; /* error number set when a remote delivery is deferred with a host error */
{ "envelope_to_remove", opt_bool, &envelope_to_remove },
{ "errors_copy", opt_stringptr, &errors_copy },
{ "errors_reply_to", opt_stringptr, &errors_reply_to },
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
{ "event_action", opt_stringptr, &event_action },
#endif
{ "exim_group", opt_gid, &exim_gid },
int on = 1;
int save_errno = 0;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
deliver_host_address = host->address;
deliver_host_port = port;
if (event_raise(tb->event_action, US"tcp:connect", NULL)) return -1;
BOOL log_fail_output;
BOOL log_defer_output;
BOOL retry_use_local_part; /* Defaults true for local, false for remote */
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
uschar *event_action; /* String to expand on notable events */
#endif
} transport_instance;
# warning "GnuTLS library version too old; define DISABLE_OCSP in Makefile"
# define DISABLE_OCSP
#endif
-#if GNUTLS_VERSION_NUMBER < 0x020a00 && defined(EXPERIMENTAL_EVENT)
+#if GNUTLS_VERSION_NUMBER < 0x020a00 && !defined(DISABLE_EVENT)
# warning "GnuTLS library version too old; tls:cert event unsupported"
-# undef EXPERIMENTAL_EVENT
+# define DISABLE_EVENT
#endif
#if GNUTLS_VERSION_NUMBER >= 0x030306
# define SUPPORT_CA_DIR
uschar *exp_tls_require_ciphers;
uschar *exp_tls_ocsp_file;
const uschar *exp_tls_verify_cert_hostnames;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
uschar *event_action;
#endif
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL,
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
NULL,
#endif
NULL,
#endif
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
/*
We use this callback to get observability and detail-level control
for an exim TLS connection (either direction), raising a tls:cert event
gnutls_certificate_server_set_request(state->session, GNUTLS_CERT_IGNORE);
}
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
if (event_action)
{
state->event_action = event_action;
}
#endif
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
if (tb->event_action)
{
state->event_action = tb->event_action;
/* only passed down to tls_error: */
host_item *host;
const uschar * verify_cert_hostnames;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
uschar * event_action;
#endif
} tls_ext_ctx_cb;
*/
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
static int
verify_event(tls_support * tlsp, X509 * cert, int depth, const uschar * dn,
BOOL *calledp, const BOOL *optionalp, const uschar * what)
ERR_clear_error();
}
#endif
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL"))
return 0; /* reject, with peercert set */
#endif
}
}
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL"))
return 0; /* reject, with peercert set */
#endif
{
X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
uschar dn[256];
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
int depth = X509_STORE_CTX_get_error_depth(x509ctx);
BOOL dummy_called, optional = FALSE;
#endif
DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", dn);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
if (verify_event(&tls_out, cert, depth, dn,
&dummy_called, &optional, US"DANE"))
return 0; /* reject, with peercert set */
cbinfo->dhparam = dhparam;
cbinfo->server_cipher_list = NULL;
cbinfo->host = host;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
cbinfo->event_action = NULL;
#endif
}
#endif
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
client_static_cbinfo->event_action = tb->event_action;
#endif
(void *)offsetof(transport_instance, driver_name) },
{ "envelope_to_add", opt_bool|opt_public,
(void *)(offsetof(transport_instance, envelope_to_add)) },
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
{ "event_action", opt_stringptr | opt_public,
(void *)offsetof(transport_instance, event_action) },
#endif
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
/*************************************************
* Post-defer action *
*************************************************/
addr->basic_errno = ERRNO_RCPT4XX;
addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
event_defer_errno = addr->more_errno;
msg_event_raise(US"msg:rcpt:host:defer", addr);
#endif
if (host->next)
log_write(0, LOG_MAIN, "H=%s [%s]: %s", host->name, host->address, addr->message);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
else
msg_event_raise(US"msg:rcpt:defer", addr);
#endif
#endif
if (!good_response) goto RESPONSE_FAILED;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
{
uschar * s;
lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes"
/* Set up confirmation if needed - applies only to SMTP */
if (
-#ifndef EXPERIMENTAL_EVENT
+#ifdef DISABLE_EVENT
LOGGING(smtp_confirmation) &&
#endif
!lmtp
(void)close(inblock.sock);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
(void) event_raise(tblock->event_action, US"tcp:close", NULL);
#endif
first_addr->basic_errno != ERRNO_TLSFAILURE)
write_logs(first_addr, host);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
if (rc == DEFER)
deferred_event_raise(first_addr, host);
#endif
&message_defer, TRUE);
if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL)
write_logs(first_addr, host);
-# ifdef EXPERIMENTAL_EVENT
+# ifndef DISABLE_EVENT
if (rc == DEFER)
deferred_event_raise(first_addr, host);
# endif
if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout)))
goto RESPONSE_FAILED;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes"
: host->dnssec==DS_NO ? US"no" : NULL;
if (event_raise(addr->transport->event_action,
if (rc == DEFER)
{
(void)close(inblock.sock);
-# ifdef EXPERIMENTAL_EVENT
+# ifndef DISABLE_EVENT
(void) event_raise(addr->transport->event_action,
US"tcp:close", NULL);
# endif
tls_close(FALSE, TRUE);
#endif
(void)close(inblock.sock);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
(void) event_raise(addr->transport->event_action,
US"tcp:close", NULL);
#endif
tls_close(FALSE, TRUE);
#endif
(void)close(inblock.sock);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
(void) event_raise(addr->transport->event_action, US"tcp:close", NULL);
#endif
}
-support Experimental_Event
+support Event
-support Experimental_Event
+support Event
support GnuTLS
-support Experimental_Event
+support Event
support OpenSSL
-# OCSP stapling, client, tpda
+# OCSP stapling, client, events
# duplicate of 5651
#
#
-# OCSP stapling, client, tpda
+# OCSP stapling, client, events
# duplicate of 5601
#
#
-# DANE client: TPDA
+# DANE client: events
#
exim -DSERVER=server -DDETAILS=ee -bd -oX PORT_D
****