* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2014 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
/* See the file NOTICE for conditions of use and distribution. */
/* Code for handling Access Control Lists (ACLs) */
ACLC_DECODE,
#endif
ACLC_DELAY,
-#ifdef WITH_OLD_DEMIME
- ACLC_DEMIME,
-#endif
#ifndef DISABLE_DKIM
ACLC_DKIM_SIGNER,
ACLC_DKIM_STATUS,
#ifdef WITH_CONTENT_SCAN
ACLC_MIME_REGEX,
#endif
+ ACLC_QUEUE,
ACLC_RATELIMIT,
ACLC_RECIPIENTS,
#ifdef WITH_CONTENT_SCAN
ACLC_VERIFY };
/* ACL conditions/modifiers: "delay", "control", "continue", "endpass",
-"message", "log_message", "log_reject_target", "logwrite", and "set" are
+"message", "log_message", "log_reject_target", "logwrite", "queue" and "set" are
modifiers that look like conditions but always return TRUE. They are used for
their side effects. */
US"decode",
#endif
US"delay",
-#ifdef WITH_OLD_DEMIME
- US"demime",
-#endif
#ifndef DISABLE_DKIM
US"dkim_signers",
US"dkim_status",
#ifdef WITH_CONTENT_SCAN
US"mime_regex",
#endif
+ US"queue",
US"ratelimit",
US"recipients",
#ifdef WITH_CONTENT_SCAN
US"regex",
#endif
US"remove_header",
- US"sender_domains", US"senders", US"set",
+ US"sender_domains",
+ US"senders",
+ US"set",
#ifdef WITH_CONTENT_SCAN
US"spam",
#endif
enum {
CONTROL_AUTH_UNADVERTISED,
- #ifdef EXPERIMENTAL_BRIGHTMAIL
+#ifdef EXPERIMENTAL_BRIGHTMAIL
CONTROL_BMI_RUN,
- #endif
+#endif
CONTROL_DEBUG,
- #ifndef DISABLE_DKIM
+#ifndef DISABLE_DKIM
CONTROL_DKIM_VERIFY,
- #endif
- #ifdef EXPERIMENTAL_DMARC
+#endif
+#ifdef EXPERIMENTAL_DMARC
CONTROL_DMARC_VERIFY,
CONTROL_DMARC_FORENSIC,
- #endif
+#endif
CONTROL_DSCP,
CONTROL_ERROR,
CONTROL_CASEFUL_LOCAL_PART,
CONTROL_QUEUE_ONLY,
CONTROL_SUBMISSION,
CONTROL_SUPPRESS_LOCAL_FIXUPS,
- #ifdef WITH_CONTENT_SCAN
+#ifdef WITH_CONTENT_SCAN
CONTROL_NO_MBOX_UNSPOOL,
- #endif
+#endif
CONTROL_FAKEDEFER,
CONTROL_FAKEREJECT,
+#ifdef SUPPORT_I18N
+ CONTROL_UTF8_DOWNCONVERT,
+#endif
CONTROL_NO_MULTILINE,
CONTROL_NO_PIPELINING,
CONTROL_NO_DELAY_FLUSH,
static uschar *controls[] = {
US"allow_auth_unadvertised",
- #ifdef EXPERIMENTAL_BRIGHTMAIL
+#ifdef EXPERIMENTAL_BRIGHTMAIL
US"bmi_run",
- #endif
+#endif
US"debug",
- #ifndef DISABLE_DKIM
+#ifndef DISABLE_DKIM
US"dkim_disable_verify",
- #endif
- #ifdef EXPERIMENTAL_DMARC
+#endif
+#ifdef EXPERIMENTAL_DMARC
US"dmarc_disable_verify",
US"dmarc_enable_forensic",
- #endif
+#endif
US"dscp",
US"error",
US"caseful_local_part",
US"queue_only",
US"submission",
US"suppress_local_fixups",
- #ifdef WITH_CONTENT_SCAN
+#ifdef WITH_CONTENT_SCAN
US"no_mbox_unspool",
- #endif
+#endif
US"fakedefer",
US"fakereject",
+#ifdef SUPPORT_I18N
+ US"utf8_downconvert",
+#endif
US"no_multiline_responses",
US"no_pipelining",
US"no_delay_flush",
TRUE, /* decode */
#endif
TRUE, /* delay */
-#ifdef WITH_OLD_DEMIME
- TRUE, /* demime */
-#endif
#ifndef DISABLE_DKIM
TRUE, /* dkim_signers */
TRUE, /* dkim_status */
#ifdef WITH_CONTENT_SCAN
TRUE, /* mime_regex */
#endif
+ TRUE, /* queue */
TRUE, /* ratelimit */
FALSE, /* recipients */
#ifdef WITH_CONTENT_SCAN
FALSE, /* decode */
#endif
TRUE, /* delay */
-#ifdef WITH_OLD_DEMIME
- FALSE, /* demime */
-#endif
#ifndef DISABLE_DKIM
FALSE, /* dkim_signers */
FALSE, /* dkim_status */
#ifdef WITH_CONTENT_SCAN
FALSE, /* mime_regex */
#endif
+ TRUE, /* queue */
FALSE, /* ratelimit */
FALSE, /* recipients */
#ifdef WITH_CONTENT_SCAN
(1<<ACL_WHERE_NOTQUIT), /* delay */
- #ifdef WITH_OLD_DEMIME
- (unsigned int)
- ~((1<<ACL_WHERE_DATA)| /* demime */
- #ifndef DISABLE_PRDR
- (1<<ACL_WHERE_PRDR)|
- #endif
- (1<<ACL_WHERE_NOTSMTP)),
- #endif
-
#ifndef DISABLE_DKIM
(unsigned int)
~(1<<ACL_WHERE_DKIM), /* dkim_signers */
~(1<<ACL_WHERE_DATA), /* dmarc_status */
#endif
- (1<<ACL_WHERE_NOTSMTP)| /* dnslists */
- (1<<ACL_WHERE_NOTSMTP_START),
+ /* Explicit key lookups can be made in non-smtp ACLs so pass
+ always and check in the verify processing itself. */
+
+ 0, /* dnslists */
(unsigned int)
~((1<<ACL_WHERE_RCPT) /* domains */
+ |(1<<ACL_WHERE_VRFY)
#ifndef DISABLE_PRDR
|(1<<ACL_WHERE_PRDR)
#endif
(unsigned int)
~((1<<ACL_WHERE_RCPT) /* local_parts */
+ |(1<<ACL_WHERE_VRFY)
#ifndef DISABLE_PRDR
|(1<<ACL_WHERE_PRDR)
#endif
~(1<<ACL_WHERE_MIME), /* mime_regex */
#endif
+ (1<<ACL_WHERE_NOTSMTP)| /* queue */
+ #ifndef DISABLE_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
+ (1<<ACL_WHERE_DATA),
+
0, /* ratelimit */
(unsigned int)
(unsigned int)
~((1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)), /* allow_auth_unadvertised */
- #ifdef EXPERIMENTAL_BRIGHTMAIL
+#ifdef EXPERIMENTAL_BRIGHTMAIL
0, /* bmi_run */
- #endif
+#endif
0, /* debug */
- #ifndef DISABLE_DKIM
+#ifndef DISABLE_DKIM
(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dkim_disable_verify */
- #ifndef DISABLE_PRDR
+# ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif
+# endif
(1<<ACL_WHERE_NOTSMTP_START),
- #endif
+#endif
- #ifdef EXPERIMENTAL_DMARC
+#ifdef EXPERIMENTAL_DMARC
(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dmarc_disable_verify */
(1<<ACL_WHERE_NOTSMTP_START),
(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dmarc_enable_forensic */
(1<<ACL_WHERE_NOTSMTP_START),
- #endif
+#endif
(1<<ACL_WHERE_NOTSMTP)|
(1<<ACL_WHERE_NOTSMTP_START)|
(1<<ACL_WHERE_PREDATA)|
(1<<ACL_WHERE_NOTSMTP_START)),
- #ifdef WITH_CONTENT_SCAN
+#ifdef WITH_CONTENT_SCAN
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* no_mbox_unspool */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
// (1<<ACL_WHERE_PRDR)| /* Not allow one user to freeze for all */
(1<<ACL_WHERE_MIME)),
- #endif
+#endif
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* fakedefer */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
- #ifndef DISABLE_PRDR
+#ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif
+#endif
(1<<ACL_WHERE_MIME)),
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* fakereject */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
- #ifndef DISABLE_PRDR
+#ifndef DISABLE_PRDR
(1<<ACL_WHERE_PRDR)|
- #endif
+#endif
(1<<ACL_WHERE_MIME)),
+#ifdef SUPPORT_I18N
+ 0, /* utf8_downconvert */
+#endif
+
(1<<ACL_WHERE_NOTSMTP)| /* no_multiline */
(1<<ACL_WHERE_NOTSMTP_START),
} control_def;
static control_def controls_list[] = {
- { US"allow_auth_unadvertised", CONTROL_AUTH_UNADVERTISED, FALSE },
+ { US"allow_auth_unadvertised", CONTROL_AUTH_UNADVERTISED, FALSE },
#ifdef EXPERIMENTAL_BRIGHTMAIL
- { US"bmi_run", CONTROL_BMI_RUN, FALSE },
+ { US"bmi_run", CONTROL_BMI_RUN, FALSE },
#endif
- { US"debug", CONTROL_DEBUG, TRUE },
+ { US"debug", CONTROL_DEBUG, TRUE },
#ifndef DISABLE_DKIM
- { US"dkim_disable_verify", CONTROL_DKIM_VERIFY, FALSE },
+ { US"dkim_disable_verify", CONTROL_DKIM_VERIFY, FALSE },
#endif
#ifdef EXPERIMENTAL_DMARC
- { US"dmarc_disable_verify", CONTROL_DMARC_VERIFY, FALSE },
- { US"dmarc_enable_forensic", CONTROL_DMARC_FORENSIC, FALSE },
+ { US"dmarc_disable_verify", CONTROL_DMARC_VERIFY, FALSE },
+ { US"dmarc_enable_forensic", CONTROL_DMARC_FORENSIC, FALSE },
#endif
- { US"dscp", CONTROL_DSCP, TRUE },
- { US"caseful_local_part", CONTROL_CASEFUL_LOCAL_PART, FALSE },
- { US"caselower_local_part", CONTROL_CASELOWER_LOCAL_PART, FALSE },
- { US"enforce_sync", CONTROL_ENFORCE_SYNC, FALSE },
- { US"freeze", CONTROL_FREEZE, TRUE },
- { US"no_callout_flush", CONTROL_NO_CALLOUT_FLUSH, FALSE },
- { US"no_delay_flush", CONTROL_NO_DELAY_FLUSH, FALSE },
- { US"no_enforce_sync", CONTROL_NO_ENFORCE_SYNC, FALSE },
- { US"no_multiline_responses", CONTROL_NO_MULTILINE, FALSE },
- { US"no_pipelining", CONTROL_NO_PIPELINING, FALSE },
- { US"queue_only", CONTROL_QUEUE_ONLY, FALSE },
+ { US"dscp", CONTROL_DSCP, TRUE },
+ { US"caseful_local_part", CONTROL_CASEFUL_LOCAL_PART, FALSE },
+ { US"caselower_local_part", CONTROL_CASELOWER_LOCAL_PART, FALSE },
+ { US"enforce_sync", CONTROL_ENFORCE_SYNC, FALSE },
+ { US"freeze", CONTROL_FREEZE, TRUE },
+ { US"no_callout_flush", CONTROL_NO_CALLOUT_FLUSH, FALSE },
+ { US"no_delay_flush", CONTROL_NO_DELAY_FLUSH, FALSE },
+ { US"no_enforce_sync", CONTROL_NO_ENFORCE_SYNC, FALSE },
+ { US"no_multiline_responses", CONTROL_NO_MULTILINE, FALSE },
+ { US"no_pipelining", CONTROL_NO_PIPELINING, FALSE },
+ { US"queue_only", CONTROL_QUEUE_ONLY, FALSE },
#ifdef WITH_CONTENT_SCAN
- { US"no_mbox_unspool", CONTROL_NO_MBOX_UNSPOOL, FALSE },
+ { US"no_mbox_unspool", CONTROL_NO_MBOX_UNSPOOL, FALSE },
#endif
- { US"fakedefer", CONTROL_FAKEDEFER, TRUE },
- { US"fakereject", CONTROL_FAKEREJECT, TRUE },
- { US"submission", CONTROL_SUBMISSION, TRUE },
+ { US"fakedefer", CONTROL_FAKEDEFER, TRUE },
+ { US"fakereject", CONTROL_FAKEREJECT, TRUE },
+ { US"submission", CONTROL_SUBMISSION, TRUE },
{ US"suppress_local_fixups", CONTROL_SUPPRESS_LOCAL_FIXUPS, FALSE },
- { US"cutthrough_delivery", CONTROL_CUTTHROUGH_DELIVERY, FALSE }
+ { US"cutthrough_delivery", CONTROL_CUTTHROUGH_DELIVERY, FALSE },
+#ifdef SUPPORT_I18N
+ { US"utf8_downconvert", CONTROL_UTF8_DOWNCONVERT, TRUE }
+#endif
};
/* Support data structures for Client SMTP Authorization. acl_verify_csa()
/* An empty string does nothing; ensure exactly one final newline. */
if (hlen <= 0) return;
-if (hstring[--hlen] != '\n')
+if (hstring[--hlen] != '\n') /* no newline */
q = string_sprintf("%s\n", hstring);
-else if (hstring[hlen-1] == '\n')
+else if (hstring[hlen-1] == '\n') /* double newline */
{
uschar * s = string_copy(hstring);
while(s[--hlen] == '\n')
for (;;)
{
- q = Ustrchr(q, '\n');
+ q = Ustrchr(q, '\n'); /* we know there was a newline */
if (*(++q) != ' ' && *q != '\t') break;
}
fn_hdrs_added(void)
{
uschar * ret = NULL;
+int size = 0;
+int ptr = 0;
header_line * h = acl_added_headers;
uschar * s;
uschar * cp;
-int size = 0;
-int ptr = 0;
if (!h) return NULL;
if (cp[1] == '\0') break;
/* contains embedded newline; needs doubling */
- ret = string_cat(ret, &size, &ptr, s, cp-s+1);
- ret = string_cat(ret, &size, &ptr, US"\n", 1);
+ ret = string_catn(ret, &size, &ptr, s, cp-s+1);
+ ret = string_catn(ret, &size, &ptr, US"\n", 1);
s = cp+1;
}
/* last bit of header */
- ret = string_cat(ret, &size, &ptr, s, cp-s+1); /* newline-sep list */
+ ret = string_catn(ret, &size, &ptr, s, cp-s+1); /* newline-sep list */
}
while((h = h->next));
test whether it was successful or not. (This is for optional verification; for
mandatory verification, the connection doesn't last this long.) */
- if (tls_in.certificate_verified) return OK;
- *user_msgptr = US"no verified certificate";
- return FAIL;
+ if (tls_in.certificate_verified) return OK;
+ *user_msgptr = US"no verified certificate";
+ return FAIL;
case VERIFY_HELO:
/* We can test the result of optional HELO verification that might have
occurred earlier. If not, we can attempt the verification now. */
- if (!helo_verified && !helo_verify_failed) smtp_verify_helo();
- return helo_verified? OK : FAIL;
+ if (!helo_verified && !helo_verify_failed) smtp_verify_helo();
+ return helo_verified? OK : FAIL;
case VERIFY_CSA:
/* Do Client SMTP Authorization checks in a separate function, and turn the
result code into user-friendly strings. */
- rc = acl_verify_csa(list);
- *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s",
+ rc = acl_verify_csa(list);
+ *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s",
csa_reason_string[rc]);
- csa_status = csa_status_string[rc];
- DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status);
- return csa_return_code[rc];
+ csa_status = csa_status_string[rc];
+ DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status);
+ return csa_return_code[rc];
case VERIFY_HDR_SYNTAX:
/* Check that all relevant header lines have the correct syntax. If there is
always). */
rc = verify_check_headers(log_msgptr);
- if (rc != OK && smtp_return_error_details && *log_msgptr != NULL)
- *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
+ if (rc != OK && *log_msgptr)
+ if (smtp_return_error_details)
+ *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
+ else
+ acl_verify_message = *log_msgptr;
return rc;
case VERIFY_HDR_NAMES_ASCII:
if (verify_header_sender)
{
int verrno;
- rc = verify_check_header_address(user_msgptr, log_msgptr, callout,
+
+ if ((rc = verify_check_header_address(user_msgptr, log_msgptr, callout,
callout_overall, callout_connect, se_mailfrom, pm_mailfrom, verify_options,
- &verrno);
- if (rc != OK)
+ &verrno)) != OK)
{
*basic_errno = verrno;
if (smtp_return_error_details)
{
- if (*user_msgptr == NULL && *log_msgptr != NULL)
+ if (!*user_msgptr && *log_msgptr)
*user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
if (rc == DEFER) acl_temp_details = TRUE;
}
specified. Caching elsewhere, for instance in the DNS resolver and in the
callout handling, should ensure that this is not terribly inefficient. */
-else if (verify_sender_address != NULL)
+else if (verify_sender_address)
{
- if ((verify_options & (vopt_callout_recipsender|vopt_callout_recippmaster))
- != 0)
+ if ((verify_options & (vopt_callout_recipsender|vopt_callout_recippmaster)))
{
*log_msgptr = US"use_sender or use_postmaster cannot be used for a "
"sender verify callout";
callout that was done previously). If the "routed" flag is not set, routing
must have failed, so we use the saved return code. */
- if (testflag(sender_vaddr, af_verify_routed)) rc = OK; else
+ if (testflag(sender_vaddr, af_verify_routed))
+ rc = OK;
+ else
{
rc = sender_vaddr->special_action;
*basic_errno = sender_vaddr->basic_errno;
uschar *save_address_data = deliver_address_data;
sender_vaddr = deliver_make_addr(verify_sender_address, TRUE);
-#ifdef EXPERIMENTAL_INTERNATIONAL
- sender_vaddr->p.utf8 = message_smtputf8;
+#ifdef SUPPORT_I18N
+ if ((sender_vaddr->prop.utf8_msg = message_smtputf8))
+ {
+ sender_vaddr->prop.utf8_downcvt = message_utf8_downconvert == 1;
+ sender_vaddr->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1;
+ }
#endif
if (no_details) setflag(sender_vaddr, af_sverify_told);
if (verify_sender_address[0] != 0)
HDEBUG(D_acl) debug_printf("----------- end verify ------------\n");
- if (rc == OK)
- {
- if (Ustrcmp(sender_vaddr->address, verify_sender_address) != 0)
- {
- DEBUG(D_acl) debug_printf("sender %s verified ok as %s\n",
- verify_sender_address, sender_vaddr->address);
- }
- else
- {
- DEBUG(D_acl) debug_printf("sender %s verified ok\n",
- verify_sender_address);
- }
- }
- else *basic_errno = sender_vaddr->basic_errno;
+ if (rc != OK)
+ *basic_errno = sender_vaddr->basic_errno;
+ else
+ DEBUG(D_acl)
+ {
+ if (Ustrcmp(sender_vaddr->address, verify_sender_address) != 0)
+ debug_printf("sender %s verified ok as %s\n",
+ verify_sender_address, sender_vaddr->address);
+ else
+ debug_printf("sender %s verified ok\n",
+ verify_sender_address);
+ }
}
- else rc = OK; /* Null sender */
+ else
+ rc = OK; /* Null sender */
/* Cache the result code */
/* Put the sender address_data value into $sender_address_data */
- sender_address_data = sender_vaddr->p.address_data;
+ sender_address_data = sender_vaddr->prop.address_data;
}
/* A recipient address just gets a straightforward verify; again we must handle
if (testflag((&addr2), af_pass_message)) acl_temp_details = TRUE;
/* Make $address_data visible */
- deliver_address_data = addr2.p.address_data;
+ deliver_address_data = addr2.prop.address_data;
}
/* We have a result from the relevant test. Handle defer overrides first. */
if (rc != OK && verify_sender_address != NULL)
{
if (rc != DEFER)
- {
*log_msgptr = *user_msgptr = US"Sender verify failed";
- }
else if (*basic_errno != ERRNO_CALLOUTDEFER)
- {
*log_msgptr = *user_msgptr = US"Could not complete sender verify";
- }
else
{
*log_msgptr = US"Could not complete sender verify callout";
sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0);
if (sender_rate_limit == NULL)
- {
- limit = -1.0;
- ss = NULL; /* compiler quietening */
- }
-else
- {
- limit = Ustrtod(sender_rate_limit, &ss);
- if (tolower(*ss) == 'k') { limit *= 1024.0; ss++; }
- else if (tolower(*ss) == 'm') { limit *= 1024.0*1024.0; ss++; }
- else if (tolower(*ss) == 'g') { limit *= 1024.0*1024.0*1024.0; ss++; }
- }
+ return ratelimit_error(log_msgptr, "sender rate limit not set");
+
+limit = Ustrtod(sender_rate_limit, &ss);
+if (tolower(*ss) == 'k') { limit *= 1024.0; ss++; }
+else if (tolower(*ss) == 'm') { limit *= 1024.0*1024.0; ss++; }
+else if (tolower(*ss) == 'g') { limit *= 1024.0*1024.0*1024.0; ss++; }
+
if (limit < 0.0 || *ss != '\0')
return ratelimit_error(log_msgptr,
"\"%s\" is not a positive number", sender_rate_limit);
{
uschar *user_message = NULL;
uschar *log_message = NULL;
-uschar *debug_tag = NULL;
-uschar *debug_opts = NULL;
int rc = OK;
#ifdef WITH_CONTENT_SCAN
int sep = -'/';
break;
case CONTROL_DEBUG:
- while (*p == '/')
{
- if (Ustrncmp(p, "/tag=", 5) == 0)
- {
- const uschar *pp = p + 5;
- while (*pp != '\0' && *pp != '/') pp++;
- debug_tag = string_copyn(p+5, pp-p-5);
- p = pp;
- }
- else if (Ustrncmp(p, "/opts=", 6) == 0)
+ uschar * debug_tag = NULL;
+ uschar * debug_opts = NULL;
+ BOOL kill = FALSE;
+
+ while (*p == '/')
{
- const uschar *pp = p + 6;
- while (*pp != '\0' && *pp != '/') pp++;
- debug_opts = string_copyn(p+6, pp-p-6);
+ const uschar * pp = p+1;
+ if (Ustrncmp(pp, "tag=", 4) == 0)
+ {
+ for (pp += 4; *pp && *pp != '/';) pp++;
+ debug_tag = string_copyn(p+5, pp-p-5);
+ }
+ else if (Ustrncmp(pp, "opts=", 5) == 0)
+ {
+ for (pp += 5; *pp && *pp != '/';) pp++;
+ debug_opts = string_copyn(p+6, pp-p-6);
+ }
+ else if (Ustrncmp(pp, "kill", 4) == 0)
+ {
+ for (pp += 4; *pp && *pp != '/';) pp++;
+ kill = TRUE;
+ }
+ else
+ while (*pp && *pp != '/') pp++;
p = pp;
}
+
+ if (kill)
+ debug_logging_stop();
+ else
+ debug_logging_activate(debug_tag, debug_opts);
}
- debug_logging_activate(debug_tag, debug_opts);
break;
case CONTROL_SUPPRESS_LOCAL_FIXUPS:
break;
case CONTROL_CUTTHROUGH_DELIVERY:
+#ifndef DISABLE_PRDR
if (prdr_requested)
+#else
+ if (0)
+#endif
/* Too hard to think about for now. We might in future cutthrough
the case where both sides handle prdr and this-node prdr acl
is "accept" */
arg, *log_msgptr);
}
return ERROR;
+
+#ifdef SUPPORT_I18N
+ case CONTROL_UTF8_DOWNCONVERT:
+ if (*p == '/')
+ {
+ if (p[1] == '1')
+ {
+ message_utf8_downconvert = 1;
+ addr->prop.utf8_downcvt = TRUE;
+ addr->prop.utf8_downcvt_maybe = FALSE;
+ p += 2;
+ break;
+ }
+ if (p[1] == '0')
+ {
+ message_utf8_downconvert = 0;
+ addr->prop.utf8_downcvt = FALSE;
+ addr->prop.utf8_downcvt_maybe = FALSE;
+ p += 2;
+ break;
+ }
+ if (p[1] == '-' && p[2] == '1')
+ {
+ message_utf8_downconvert = -1;
+ addr->prop.utf8_downcvt = FALSE;
+ addr->prop.utf8_downcvt_maybe = TRUE;
+ p += 3;
+ break;
+ }
+ *log_msgptr = US"bad option value for control=utf8_downconvert";
+ }
+ else
+ {
+ message_utf8_downconvert = 1;
+ addr->prop.utf8_downcvt = TRUE;
+ addr->prop.utf8_downcvt_maybe = FALSE;
+ break;
+ }
+ return ERROR;
+#endif
+
}
break;
}
/* Run the dcc backend. */
rc = dcc_process(&ss);
/* Modify return code based upon the existance of options. */
- while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
- != NULL) {
+ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER)
- {
- /* FAIL so that the message is passed to the next ACL */
- rc = FAIL;
- }
- }
+ rc = FAIL; /* FAIL so that the message is passed to the next ACL */
}
break;
#endif
debug_printf("delay skipped in -bh checking mode\n");
}
+ /* NOTE 1: Remember that we may be
+ dealing with stdin/stdout here, in addition to TCP/IP connections.
+ Also, delays may be specified for non-SMTP input, where smtp_out and
+ smtp_in will be NULL. Whatever is done must work in all cases.
+
+ NOTE 2: The added feature of flushing the output before a delay must
+ apply only to SMTP input. Hence the test for smtp_out being non-NULL.
+ */
+
+ else
+ {
+ if (smtp_out != NULL && !disable_delay_flush)
+ mac_smtp_fflush();
+
+#if !defined(NO_POLL_H) && defined (POLLRDHUP)
+ {
+ struct pollfd p;
+ nfds_t n = 0;
+ if (smtp_out)
+ {
+ p.fd = fileno(smtp_out);
+ p.events = POLLRDHUP;
+ n = 1;
+ }
+ if (poll(&p, n, delay*1000) > 0)
+ HDEBUG(D_acl) debug_printf("delay cancelled by peer close\n");
+ }
+#else
/* It appears to be impossible to detect that a TCP/IP connection has
gone away without reading from it. This means that we cannot shorten
the delay below if the client goes away, because we cannot discover
Exim process is not held up unnecessarily. However, it seems that we
can't. The poll() function does not do the right thing, and in any case
it is not always available.
-
- NOTE 1: If ever this state of affairs changes, remember that we may be
- dealing with stdin/stdout here, in addition to TCP/IP connections.
- Also, delays may be specified for non-SMTP input, where smtp_out and
- smtp_in will be NULL. Whatever is done must work in all cases.
-
- NOTE 2: The added feature of flushing the output before a delay must
- apply only to SMTP input. Hence the test for smtp_out being non-NULL.
*/
- else
- {
- if (smtp_out != NULL && !disable_delay_flush) mac_smtp_fflush();
while (delay > 0) delay = sleep(delay);
+#endif
}
}
}
break;
- #ifdef WITH_OLD_DEMIME
- case ACLC_DEMIME:
- rc = demime(&arg);
- break;
- #endif
-
#ifndef DISABLE_DKIM
case ACLC_DKIM_SIGNER:
if (dkim_cur_signer != NULL)
rc = match_isinlist(dkim_cur_signer,
&arg,0,NULL,NULL,MCL_STRING,TRUE,NULL);
else
- rc = FAIL;
+ rc = FAIL;
break;
case ACLC_DKIM_STATUS:
#endif
case ACLC_DNSLISTS:
- rc = verify_check_dnsbl(&arg);
+ rc = verify_check_dnsbl(where, &arg, log_msgptr);
break;
case ACLC_DOMAINS:
rc = verify_check_this_host(&arg, sender_host_cache, NULL,
(sender_host_address == NULL)? US"" : sender_host_address,
CUSS &host_data);
- if (host_data != NULL) host_data = string_copy_malloc(host_data);
+ if (rc == DEFER) *log_msgptr = search_error_message;
+ if (host_data) host_data = string_copy_malloc(host_data);
break;
case ACLC_LOCAL_PARTS:
int sep = 0;
const uschar *s = arg;
uschar *ss;
- while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size))
- != NULL)
+ while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)))
{
if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN;
else if (Ustrcmp(ss, "panic") == 0) logbits |= LOG_PANIC;
break;
#endif
+ case ACLC_QUEUE:
+ queue_name = string_copy_malloc(arg);
+ break;
+
case ACLC_RATELIMIT:
rc = acl_ratelimit(arg, where, log_msgptr);
break;
case ACLC_SET:
{
int old_pool = store_pool;
- if (cb->u.varname[0] == 'c') store_pool = POOL_PERM;
+ if ( cb->u.varname[0] == 'c'
+#ifndef DISABLE_EVENT
+ || event_name /* An event is being delivered */
+#endif
+ )
+ store_pool = POOL_PERM;
acl_var_create(cb->u.varname)->data.ptr = string_copy(arg);
store_pool = old_pool;
}
case ACLC_VERIFY:
rc = acl_verify(where, addr, arg, user_msgptr, log_msgptr, basic_errno);
- acl_verify_message = *user_msgptr;
+ if (*user_msgptr)
+ acl_verify_message = *user_msgptr;
if (verb == ACL_WARN) *user_msgptr = NULL;
break;
case ACL_WARN:
if (cond == OK)
acl_warn(where, *user_msgptr, *log_msgptr);
- else if (cond == DEFER && (log_extra_selector & LX_acl_warn_skipped) != 0)
+ else if (cond == DEFER && LOGGING(acl_warn_skipped))
log_write(0, LOG_MAIN, "%s Warning: ACL \"warn\" statement skipped: "
"condition test deferred%s%s", host_and_ident(TRUE),
(*log_msgptr == NULL)? US"" : US": ",
log_reject_target = LOG_MAIN|LOG_REJECT;
#ifndef DISABLE_PRDR
-if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR)
+if (where==ACL_WHERE_RCPT || where==ACL_WHERE_VRFY || where==ACL_WHERE_PRDR)
#else
-if (where == ACL_WHERE_RCPT)
+if (where==ACL_WHERE_RCPT || where==ACL_WHERE_VRFY)
#endif
{
adb = address_defaults;
*log_msgptr = US"defer in percent_hack_domains check";
return DEFER;
}
-#ifdef EXPERIMENTAL_INTERNATIONAL
- addr->p.utf8 = message_smtputf8;
+#ifdef SUPPORT_I18N
+ if ((addr->prop.utf8_msg = message_smtputf8))
+ {
+ addr->prop.utf8_downcvt = message_utf8_downconvert == 1;
+ addr->prop.utf8_downcvt_maybe = message_utf8_downconvert == -1;
+ }
#endif
deliver_domain = addr->domain;
deliver_localpart = addr->local_part;
and rcpt acl returned accept,
and first recipient (cancel on any subsequents)
open one now and run it up to RCPT acceptance.
-A failed verify should cancel cutthrough request.
-
+A failed verify should cancel cutthrough request,
+and will pass the fail to the originator.
Initial implementation: dual-write to spool.
Assume the rxd datastream is now being copied byte-for-byte to an open cutthrough connection.
#ifndef DISABLE_PRDR
case ACL_WHERE_PRDR:
#endif
- if (rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt)
- open_cutthrough_connection(addr);
+ if (host_checking_callout) /* -bhc mode */
+ cancel_cutthrough_connection("host-checking mode");
+ else if (rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt)
+ rc = open_cutthrough_connection(addr);
break;
case ACL_WHERE_PREDATA:
- if( rc == OK )
+ if (rc == OK)
cutthrough_predata();
else
cancel_cutthrough_connection("predata acl not ok");