Remember the fail reason for verify=headers_syntax. Bug 264
[exim.git] / src / src / acl.c
index 18119ecfada9da97a575cde731b15e8b1b2b1aef..064ee6ccbc3f12c85f693b7847ddc6edf9baa2b8 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2014 */
+/* Copyright (c) University of Cambridge 1995 - 2015 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Code for handling Access Control Lists (ACLs) */
@@ -181,17 +181,17 @@ that follows! */
 
 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,
@@ -203,11 +203,14 @@ enum {
   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 EXPERIMENTAL_INTERNATIONAL
+  CONTROL_UTF8_DOWNCONVERT,
+#endif
   CONTROL_NO_MULTILINE,
   CONTROL_NO_PIPELINING,
   CONTROL_NO_DELAY_FLUSH,
@@ -221,17 +224,17 @@ and should be tidied up. */
 
 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",
@@ -243,11 +246,14 @@ static uschar *controls[] = {
   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 EXPERIMENTAL_INTERNATIONAL
+  US"utf8_downconvert",
+#endif
   US"no_multiline_responses",
   US"no_pipelining",
   US"no_delay_flush",
@@ -600,26 +606,26 @@ static unsigned int control_forbids[] = {
   (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)|
@@ -663,30 +669,34 @@ static unsigned int control_forbids[] = {
     (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 EXPERIMENTAL_INTERNATIONAL
+  0,                                               /* utf8_downconvert */
+#endif
+
   (1<<ACL_WHERE_NOTSMTP)|                          /* no_multiline */
     (1<<ACL_WHERE_NOTSMTP_START),
 
@@ -709,37 +719,40 @@ typedef struct control_def {
 } 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 EXPERIMENTAL_INTERNATIONAL
+  { US"utf8_downconvert",        CONTROL_UTF8_DOWNCONVERT,      TRUE }
+#endif
   };
 
 /* Support data structures for Client SMTP Authorization. acl_verify_csa()
@@ -1790,27 +1803,27 @@ switch(vp->value)
     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
@@ -1819,8 +1832,11 @@ switch(vp->value)
     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:
@@ -2079,6 +2095,13 @@ else if (verify_sender_address != NULL)
     uschar *save_address_data = deliver_address_data;
 
     sender_vaddr = deliver_make_addr(verify_sender_address, TRUE);
+#ifdef EXPERIMENTAL_INTERNATIONAL
+    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)
       {
@@ -2135,7 +2158,7 @@ else if (verify_sender_address != NULL)
 
   /* 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
@@ -2165,7 +2188,7 @@ else
   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. */
@@ -2184,13 +2207,9 @@ sender_verified_failed to the address item that actually failed. */
 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";
@@ -3356,7 +3375,11 @@ for (; cb != NULL; cb = cb->next)
        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" */
@@ -3378,6 +3401,47 @@ for (; cb != NULL; cb = cb->next)
                                        arg, *log_msgptr);
          }
        return ERROR;
+
+    #ifdef EXPERIMENTAL_INTERNATIONAL
+       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;
       }
@@ -3391,14 +3455,9 @@ for (; cb != NULL; cb = cb->next)
       /* 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
@@ -3428,6 +3487,34 @@ for (; cb != NULL; cb = cb->next)
             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
@@ -3437,20 +3524,10 @@ for (; cb != NULL; cb = cb->next)
         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
           }
         }
       }
@@ -3530,7 +3607,8 @@ for (; cb != NULL; cb = cb->next)
     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:
@@ -3545,8 +3623,7 @@ for (; cb != NULL; cb = cb->next)
       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;
@@ -3714,7 +3791,8 @@ for (; cb != NULL; cb = cb->next)
 
     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;
 
@@ -4077,19 +4155,12 @@ while (acl != NULL)
   int cond;
   int basic_errno = 0;
   BOOL endpass_seen = FALSE;
+  BOOL acl_quit_check = level == 0
+    && (where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT);
 
   *log_msgptr = *user_msgptr = NULL;
   acl_temp_details = FALSE;
 
-  if ((where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT) &&
-      acl->verb != ACL_ACCEPT &&
-      acl->verb != ACL_WARN)
-    {
-    *log_msgptr = string_sprintf("\"%s\" is not allowed in a QUIT or not-QUIT ACL",
-      verbs[acl->verb]);
-    return ERROR;
-    }
-
   HDEBUG(D_acl) debug_printf("processing \"%s\"\n", verbs[acl->verb]);
 
   /* Clear out any search error message from a previous check before testing
@@ -4170,6 +4241,7 @@ while (acl != NULL)
     if (cond == OK)
       {
       HDEBUG(D_acl) debug_printf("end of %s: DEFER\n", acl_name);
+      if (acl_quit_check) goto badquit;
       acl_temp_details = TRUE;
       return DEFER;
       }
@@ -4179,6 +4251,7 @@ while (acl != NULL)
     if (cond == OK)
       {
       HDEBUG(D_acl) debug_printf("end of %s: DENY\n", acl_name);
+      if (acl_quit_check) goto badquit;
       return FAIL;
       }
     break;
@@ -4187,6 +4260,7 @@ while (acl != NULL)
     if (cond == OK || cond == DISCARD)
       {
       HDEBUG(D_acl) debug_printf("end of %s: DISCARD\n", acl_name);
+      if (acl_quit_check) goto badquit;
       return DISCARD;
       }
     if (endpass_seen)
@@ -4200,6 +4274,7 @@ while (acl != NULL)
     if (cond == OK)
       {
       HDEBUG(D_acl) debug_printf("end of %s: DROP\n", acl_name);
+      if (acl_quit_check) goto badquit;
       return FAIL_DROP;
       }
     break;
@@ -4208,6 +4283,7 @@ while (acl != NULL)
     if (cond != OK)
       {
       HDEBUG(D_acl) debug_printf("end of %s: not OK\n", acl_name);
+      if (acl_quit_check) goto badquit;
       return cond;
       }
     break;
@@ -4215,7 +4291,7 @@ while (acl != NULL)
     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": ",
@@ -4238,6 +4314,11 @@ while (acl != NULL)
 
 HDEBUG(D_acl) debug_printf("end of %s: implicit DENY\n", acl_name);
 return FAIL;
+
+badquit:
+  *log_msgptr = string_sprintf("QUIT or not-QUIT teplevel ACL may not fail "
+    "('%s' verb used incorrectly)", verbs[acl->verb]);
+  return ERROR;
 }
 
 
@@ -4379,6 +4460,13 @@ if (where == ACL_WHERE_RCPT)
     *log_msgptr = US"defer in percent_hack_domains check";
     return DEFER;
     }
+#ifdef EXPERIMENTAL_INTERNATIONAL
+  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;
   }