Actually reap node2 process in redis cluster test
[exim.git] / src / src / receive.c
index f181f1a51e1a313b9338e3d4c63faa819e1877df..4e1dca5c57034b9e14e63a7ec173cb299d21522a 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Code for receiving a message and setting up spool files. */
@@ -1023,7 +1023,8 @@ int ch;
 
 /* Remember that this message uses wireformat. */
 
-DEBUG(D_receive) debug_printf("CHUNKING: writing spoolfile in wire format\n");
+DEBUG(D_receive) debug_printf("CHUNKING: %s\n",
+       fout ? "writing spoolfile in wire format" : "flushing input");
 spool_file_wireformat = TRUE;
 
 for (;;)
@@ -1033,6 +1034,7 @@ for (;;)
     unsigned len = MAX(chunking_data_left, thismessage_size_limit - message_size + 1);
     uschar * buf = bdat_getbuf(&len);
 
+    if (!buf) return END_EOF;
     message_size += len;
     if (fout && fwrite(buf, len, 1, fout) != 1) return END_WERROR;
     }
@@ -1077,9 +1079,10 @@ Returns:     nothing
 void
 receive_swallow_smtp(void)
 {
-/*XXX CHUNKING: not enough.  read chunks until RSET? */
 if (message_ended >= END_NOTENDED)
-  message_ended = read_message_data_smtp(NULL);
+  message_ended = chunking_state <= CHUNKING_OFFERED
+     ? read_message_data_smtp(NULL)
+     : read_message_bdat_smtp_wire(NULL);
 }
 
 
@@ -1221,48 +1224,49 @@ for (h = acl_added_headers; h; h = next)
   switch(h->type)
     {
     case htype_add_top:
-    h->next = header_list;
-    header_list = h;
-    DEBUG(D_receive|D_acl) debug_printf_indent("  (at top)");
-    break;
+      h->next = header_list;
+      header_list = h;
+      DEBUG(D_receive|D_acl) debug_printf_indent("  (at top)");
+      break;
 
     case htype_add_rec:
-    if (last_received == NULL)
-      {
-      last_received = header_list;
-      while (!header_testname(last_received, US"Received", 8, FALSE))
-        last_received = last_received->next;
-      while (last_received->next != NULL &&
-             header_testname(last_received->next, US"Received", 8, FALSE))
-        last_received = last_received->next;
-      }
-    h->next = last_received->next;
-    last_received->next = h;
-    DEBUG(D_receive|D_acl) debug_printf_indent("  (after Received:)");
-    break;
+      if (!last_received)
+       {
+       last_received = header_list;
+       while (!header_testname(last_received, US"Received", 8, FALSE))
+         last_received = last_received->next;
+       while (last_received->next &&
+              header_testname(last_received->next, US"Received", 8, FALSE))
+         last_received = last_received->next;
+       }
+      h->next = last_received->next;
+      last_received->next = h;
+      DEBUG(D_receive|D_acl) debug_printf_indent("  (after Received:)");
+      break;
 
     case htype_add_rfc:
-    /* add header before any header which is NOT Received: or Resent- */
-    last_received = header_list;
-    while ( (last_received->next != NULL) &&
-            ( (header_testname(last_received->next, US"Received", 8, FALSE)) ||
-              (header_testname_incomplete(last_received->next, US"Resent-", 7, FALSE)) ) )
-              last_received = last_received->next;
-    /* last_received now points to the last Received: or Resent-* header
-       in an uninterrupted chain of those header types (seen from the beginning
-       of all headers. Our current header must follow it. */
-    h->next = last_received->next;
-    last_received->next = h;
-    DEBUG(D_receive|D_acl) debug_printf_indent("  (before any non-Received: or Resent-*: header)");
-    break;
+      /* add header before any header which is NOT Received: or Resent- */
+      last_received = header_list;
+      while ( last_received->next &&
+             ( (header_testname(last_received->next, US"Received", 8, FALSE)) ||
+               (header_testname_incomplete(last_received->next, US"Resent-", 7, FALSE)) ) )
+               last_received = last_received->next;
+      /* last_received now points to the last Received: or Resent-* header
+        in an uninterrupted chain of those header types (seen from the beginning
+        of all headers. Our current header must follow it. */
+      h->next = last_received->next;
+      last_received->next = h;
+      DEBUG(D_receive|D_acl) debug_printf_indent("  (before any non-Received: or Resent-*: header)");
+      break;
 
     default:
-    h->next = NULL;
-    header_last->next = h;
-    break;
+      h->next = NULL;
+      header_last->next = h;
+      DEBUG(D_receive|D_acl) debug_printf_indent("  ");
+      break;
     }
 
-  if (h->next == NULL) header_last = h;
+  if (!h->next) header_last = h;
 
   /* Check for one of the known header types (From:, To:, etc.) though in
   practice most added headers are going to be "other". Lower case
@@ -1273,7 +1277,7 @@ for (h = acl_added_headers; h; h = next)
   h->type = header_checkname(h, FALSE);
   if (h->type >= 'a') h->type = htype_other;
 
-  DEBUG(D_receive|D_acl) debug_printf_indent("  %s", header_last->text);
+  DEBUG(D_receive|D_acl) debug_printf("%s", h->text);
   }
 
 acl_added_headers = NULL;
@@ -1346,7 +1350,7 @@ run_mime_acl(uschar *acl, BOOL *smtp_yield_ptr, uschar **smtp_reply_ptr,
   uschar **blackholed_by_ptr)
 {
 FILE *mbox_file;
-uschar rfc822_file_path[2048];
+uschar * rfc822_file_path = NULL;
 unsigned long mbox_size;
 header_line *my_headerlist;
 uschar *user_msg, *log_msg;
@@ -1354,8 +1358,6 @@ int mime_part_count_buffer = -1;
 uschar * mbox_filename;
 int rc = OK;
 
-memset(CS rfc822_file_path,0,2048);
-
 /* check if it is a MIME message */
 
 for (my_headerlist = header_list; my_headerlist; my_headerlist = my_headerlist->next)
@@ -1395,7 +1397,7 @@ mime_part_count = -1;
 rc = mime_acl_check(acl, mbox_file, NULL, &user_msg, &log_msg);
 (void)fclose(mbox_file);
 
-if (Ustrlen(rfc822_file_path) > 0)
+if (rfc822_file_path)
   {
   mime_part_count = mime_part_count_buffer;
 
@@ -1403,36 +1405,31 @@ if (Ustrlen(rfc822_file_path) > 0)
     {
     log_write(0, LOG_PANIC,
          "acl_smtp_mime: can't unlink RFC822 spool file, skipping.");
-      goto END_MIME_ACL;
+    goto END_MIME_ACL;
     }
+  rfc822_file_path = NULL;
   }
 
 /* check if we must check any message/rfc822 attachments */
 if (rc == OK)
   {
-  uschar * scandir;
+  uschar * scandir = string_copyn(mbox_filename,
+             Ustrrchr(mbox_filename, '/') - mbox_filename);
   struct dirent * entry;
   DIR * tempdir;
 
-  scandir = string_copyn(mbox_filename, Ustrrchr(mbox_filename, '/') - mbox_filename);
-
-  tempdir = opendir(CS scandir);
-  for (;;)
-    {
-    if (!(entry = readdir(tempdir)))
-      break;
+  for (tempdir = opendir(CS scandir); entry = readdir(tempdir); )
     if (strncmpic(US entry->d_name, US"__rfc822_", 9) == 0)
       {
-      (void) string_format(rfc822_file_path, sizeof(rfc822_file_path),
-       "%s/%s", scandir, entry->d_name);
-      DEBUG(D_receive) debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n",
-       rfc822_file_path);
+      rfc822_file_path = string_sprintf("%s/%s", scandir, entry->d_name);
+      DEBUG(D_receive)
+       debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n",
+         rfc822_file_path);
       break;
       }
-    }
   closedir(tempdir);
 
-  if (entry)
+  if (rfc822_file_path)
     {
     if ((mbox_file = Ufopen(rfc822_file_path, "rb")))
       {
@@ -1461,10 +1458,10 @@ else if (rc != OK)
 #ifdef EXPERIMENTAL_DCC
   dcc_ok = 0;
 #endif
-  if (  smtp_input
-     && smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0)
+  if (smtp_input)
     {
-    *smtp_yield_ptr = FALSE;    /* No more messages after dropped connection */
+    if (smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0)
+      *smtp_yield_ptr = FALSE;  /* No more messages after dropped connection */
     *smtp_reply_ptr = US"";     /* Indicate reply already sent */
     }
   message_id[0] = 0;            /* Indicate no message accepted */
@@ -1747,7 +1744,7 @@ message id creation below. */
 
 /* For other uses of the received time we can operate with granularity of one
 second, and for that we use the global variable received_time. This is for
-things like ultimate message timeouts.XXX */
+things like ultimate message timeouts. */
 
 received_time = message_id_tv;
 
@@ -1810,8 +1807,8 @@ for (;;)
   (and sometimes lunatic messages can have ones that are 100s of K long) we
   call store_release() for strings that have been copied - if the string is at
   the start of a block (and therefore the only thing in it, because we aren't
-  doing any other gets), the block gets freed. We can only do this because we
-  know there are no other calls to store_get() going on. */
+  doing any other gets), the block gets freed. We can only do this release if
+  there were no allocations since the once that we want to free. */
 
   if (ptr >= header_size - 4)
     {
@@ -1819,12 +1816,7 @@ for (;;)
     /* header_size += 256; */
     header_size *= 2;
     if (!store_extend(next->text, oldsize, header_size))
-      {
-      uschar *newtext = store_get(header_size);
-      memcpy(newtext, next->text, ptr);
-      store_release(next->text);
-      next->text = newtext;
-      }
+      next->text = store_newblock(next->text, header_size, ptr);
     }
 
   /* Cope with receiving a binary zero. There is dispute about whether
@@ -1864,7 +1856,7 @@ for (;;)
   prevent further reading), and break out of the loop, having freed the
   empty header, and set next = NULL to indicate no data line. */
 
-  if (ptr == 0 && ch == '.' && (smtp_input || dot_ends))
+  if (ptr == 0 && ch == '.' && dot_ends)
     {
     ch = (receive_getc)(GETC_BUFFER_UNLIMITED);
     if (ch == '\r')
@@ -2051,32 +2043,30 @@ for (;;)
   these lines in SMTP messages. There is now an option to ignore them from
   specified hosts or networks. Sigh. */
 
-  if (header_last == header_list &&
-       (!smtp_input
-         ||
-         (sender_host_address != NULL &&
-           verify_check_host(&ignore_fromline_hosts) == OK)
-         ||
-         (sender_host_address == NULL && ignore_fromline_local)
-       ) &&
-       regex_match_and_setup(regex_From, next->text, 0, -1))
+  if (  header_last == header_list
+     && (  !smtp_input
+        || (  sender_host_address
+          && verify_check_host(&ignore_fromline_hosts) == OK
+          )
+        || (!sender_host_address && ignore_fromline_local)
+        )
+     && regex_match_and_setup(regex_From, next->text, 0, -1)
+     )
     {
     if (!sender_address_forced)
       {
       uschar *uucp_sender = expand_string(uucp_from_sender);
-      if (uucp_sender == NULL)
-        {
+      if (!uucp_sender)
         log_write(0, LOG_MAIN|LOG_PANIC,
           "expansion of \"%s\" failed after matching "
           "\"From \" line: %s", uucp_from_sender, expand_string_message);
-        }
       else
         {
         int start, end, domain;
         uschar *errmess;
         uschar *newsender = parse_extract_address(uucp_sender, &errmess,
           &start, &end, &domain, TRUE);
-        if (newsender != NULL)
+        if (newsender)
           {
           if (domain == 0 && newsender[0] != 0)
             newsender = rewrite_address_qualify(newsender, FALSE);
@@ -2171,13 +2161,11 @@ for (;;)
         }
 
       else
-        {
         give_local_error(ERRMESS_VLONGHDRLINE,
           string_sprintf("message header line longer than %d characters "
            "received: message not accepted", header_line_maxsize), US"",
            error_rc, stdin, header_list->next);
         /* Does not return */
-        }
       }
 
     /* Note if any resent- fields exist. */
@@ -2263,119 +2251,119 @@ for (h = header_list->next; h; h = h->next)
   switch (header_checkname(h, is_resent))
     {
     case htype_bcc:
-    h->type = htype_bcc;        /* Both Bcc: and Resent-Bcc: */
-    break;
+      h->type = htype_bcc;        /* Both Bcc: and Resent-Bcc: */
+      break;
 
     case htype_cc:
-    h->type = htype_cc;         /* Both Cc: and Resent-Cc: */
-    break;
+      h->type = htype_cc;         /* Both Cc: and Resent-Cc: */
+      break;
 
-    /* Record whether a Date: or Resent-Date: header exists, as appropriate. */
+      /* Record whether a Date: or Resent-Date: header exists, as appropriate. */
 
     case htype_date:
-    if (!resents_exist || is_resent) date_header_exists = TRUE;
-    break;
+      if (!resents_exist || is_resent) date_header_exists = TRUE;
+      break;
 
-    /* Same comments as about Return-Path: below. */
+      /* Same comments as about Return-Path: below. */
 
     case htype_delivery_date:
-    if (delivery_date_remove) h->type = htype_old;
-    break;
+      if (delivery_date_remove) h->type = htype_old;
+      break;
 
-    /* Same comments as about Return-Path: below. */
+      /* Same comments as about Return-Path: below. */
 
     case htype_envelope_to:
-    if (envelope_to_remove) h->type = htype_old;
-    break;
+      if (envelope_to_remove) h->type = htype_old;
+      break;
 
-    /* Mark all "From:" headers so they get rewritten. Save the one that is to
-    be used for Sender: checking. For Sendmail compatibility, if the "From:"
-    header consists of just the login id of the user who called Exim, rewrite
-    it with the gecos field first. Apply this rule to Resent-From: if there
-    are resent- fields. */
+      /* Mark all "From:" headers so they get rewritten. Save the one that is to
+      be used for Sender: checking. For Sendmail compatibility, if the "From:"
+      header consists of just the login id of the user who called Exim, rewrite
+      it with the gecos field first. Apply this rule to Resent-From: if there
+      are resent- fields. */
 
     case htype_from:
-    h->type = htype_from;
-    if (!resents_exist || is_resent)
-      {
-      from_header = h;
-      if (!smtp_input)
-        {
-        int len;
-        uschar *s = Ustrchr(h->text, ':') + 1;
-        while (isspace(*s)) s++;
-        len = h->slen - (s - h->text) - 1;
-        if (Ustrlen(originator_login) == len &&
-           strncmpic(s, originator_login, len) == 0)
-          {
-          uschar *name = is_resent? US"Resent-From" : US"From";
-          header_add(htype_from, "%s: %s <%s@%s>\n", name, originator_name,
-            originator_login, qualify_domain_sender);
-          from_header = header_last;
-          h->type = htype_old;
-          DEBUG(D_receive|D_rewrite)
-            debug_printf("rewrote \"%s:\" header using gecos\n", name);
-         }
-        }
-      }
-    break;
+      h->type = htype_from;
+      if (!resents_exist || is_resent)
+       {
+       from_header = h;
+       if (!smtp_input)
+         {
+         int len;
+         uschar *s = Ustrchr(h->text, ':') + 1;
+         while (isspace(*s)) s++;
+         len = h->slen - (s - h->text) - 1;
+         if (Ustrlen(originator_login) == len &&
+             strncmpic(s, originator_login, len) == 0)
+           {
+           uschar *name = is_resent? US"Resent-From" : US"From";
+           header_add(htype_from, "%s: %s <%s@%s>\n", name, originator_name,
+             originator_login, qualify_domain_sender);
+           from_header = header_last;
+           h->type = htype_old;
+           DEBUG(D_receive|D_rewrite)
+             debug_printf("rewrote \"%s:\" header using gecos\n", name);
+          }
+         }
+       }
+      break;
 
-    /* Identify the Message-id: header for generating "in-reply-to" in the
-    autoreply transport. For incoming logging, save any resent- value. In both
-    cases, take just the first of any multiples. */
+      /* Identify the Message-id: header for generating "in-reply-to" in the
+      autoreply transport. For incoming logging, save any resent- value. In both
+      cases, take just the first of any multiples. */
 
     case htype_id:
-    if (msgid_header == NULL && (!resents_exist || is_resent))
-      {
-      msgid_header = h;
-      h->type = htype_id;
-      }
-    break;
+      if (!msgid_header && (!resents_exist || is_resent))
+       {
+       msgid_header = h;
+       h->type = htype_id;
+       }
+      break;
 
-    /* Flag all Received: headers */
+      /* Flag all Received: headers */
 
     case htype_received:
-    h->type = htype_received;
-    received_count++;
-    break;
+      h->type = htype_received;
+      received_count++;
+      break;
 
-    /* "Reply-to:" is just noted (there is no resent-reply-to field) */
+      /* "Reply-to:" is just noted (there is no resent-reply-to field) */
 
     case htype_reply_to:
-    h->type = htype_reply_to;
-    break;
+      h->type = htype_reply_to;
+      break;
 
-    /* The Return-path: header is supposed to be added to messages when
-    they leave the SMTP system. We shouldn't receive messages that already
-    contain Return-path. However, since Exim generates Return-path: on
-    local delivery, resent messages may well contain it. We therefore
-    provide an option (which defaults on) to remove any Return-path: headers
-    on input. Removal actually means flagging as "old", which prevents the
-    header being transmitted with the message. */
+      /* The Return-path: header is supposed to be added to messages when
+      they leave the SMTP system. We shouldn't receive messages that already
+      contain Return-path. However, since Exim generates Return-path: on
+      local delivery, resent messages may well contain it. We therefore
+      provide an option (which defaults on) to remove any Return-path: headers
+      on input. Removal actually means flagging as "old", which prevents the
+      header being transmitted with the message. */
 
     case htype_return_path:
-    if (return_path_remove) h->type = htype_old;
+      if (return_path_remove) h->type = htype_old;
 
-    /* If we are testing a mail filter file, use the value of the
-    Return-Path: header to set up the return_path variable, which is not
-    otherwise set. However, remove any <> that surround the address
-    because the variable doesn't have these. */
+      /* If we are testing a mail filter file, use the value of the
+      Return-Path: header to set up the return_path variable, which is not
+      otherwise set. However, remove any <> that surround the address
+      because the variable doesn't have these. */
 
-    if (filter_test != FTEST_NONE)
-      {
-      uschar *start = h->text + 12;
-      uschar *end = start + Ustrlen(start);
-      while (isspace(*start)) start++;
-      while (end > start && isspace(end[-1])) end--;
-      if (*start == '<' && end[-1] == '>')
-        {
-        start++;
-        end--;
-        }
-      return_path = string_copyn(start, end - start);
-      printf("Return-path taken from \"Return-path:\" header line\n");
-      }
-    break;
+      if (filter_test != FTEST_NONE)
+       {
+       uschar *start = h->text + 12;
+       uschar *end = start + Ustrlen(start);
+       while (isspace(*start)) start++;
+       while (end > start && isspace(end[-1])) end--;
+       if (*start == '<' && end[-1] == '>')
+         {
+         start++;
+         end--;
+         }
+       return_path = string_copyn(start, end - start);
+       printf("Return-path taken from \"Return-path:\" header line\n");
+       }
+      break;
 
     /* If there is a "Sender:" header and the message is locally originated,
     and from an untrusted caller and suppress_local_fixups is not set, or if we
@@ -2389,31 +2377,29 @@ for (h = header_list->next; h; h = h->next)
     set.) */
 
     case htype_sender:
-    h->type = ((!active_local_sender_retain &&
-                (
-                (sender_local && !trusted_caller && !suppress_local_fixups)
-                  || submission_mode
-                )
-               ) &&
-               (!resents_exist||is_resent))?
-      htype_old : htype_sender;
-    break;
+      h->type =    !active_local_sender_retain
+               && (  sender_local && !trusted_caller && !suppress_local_fixups
+                  || submission_mode
+                  )
+               && (!resents_exist || is_resent)
+       ? htype_old : htype_sender;
+      break;
 
-    /* Remember the Subject: header for logging. There is no Resent-Subject */
+      /* Remember the Subject: header for logging. There is no Resent-Subject */
 
     case htype_subject:
-    subject_header = h;
-    break;
+      subject_header = h;
+      break;
 
-    /* "To:" gets flagged, and the existence of a recipient header is noted,
-    whether it's resent- or not. */
+      /* "To:" gets flagged, and the existence of a recipient header is noted,
+      whether it's resent- or not. */
 
     case htype_to:
-    h->type = htype_to;
-    /****
-    to_or_cc_header_exists = TRUE;
-    ****/
-    break;
+      h->type = htype_to;
+      /****
+      to_or_cc_header_exists = TRUE;
+      ****/
+      break;
     }
   }
 
@@ -2633,7 +2619,7 @@ checked when it was read, to ensure it isn't too big. The timing granularity is
 left in id_resolution so that an appropriate wait can be done after receiving
 the message, if necessary (we hope it won't be). */
 
-if (host_number_string != NULL)
+if (host_number_string)
   {
   id_resolution = (BASE_62 == 62)? 5000 : 10000;
   sprintf(CS(message_id + MESSAGE_ID_LENGTH - 3), "-%2s",
@@ -2669,9 +2655,8 @@ one, but only for local (without suppress_local_fixups) or submission mode
 messages. This can be user-configured if required, but we had better flatten
 any illegal characters therein. */
 
-if (msgid_header == NULL &&
-      ((sender_host_address == NULL && !suppress_local_fixups)
-        || submission_mode))
+if (  !msgid_header
+   && ((!sender_host_address && !suppress_local_fixups) || submission_mode))
   {
   uschar *p;
   uschar *id_text = US"";
@@ -2679,20 +2664,20 @@ if (msgid_header == NULL &&
 
   /* Permit only letters, digits, dots, and hyphens in the domain */
 
-  if (message_id_domain != NULL)
+  if (message_id_domain)
     {
     uschar *new_id_domain = expand_string(message_id_domain);
-    if (new_id_domain == NULL)
+    if (!new_id_domain)
       {
       if (!expand_string_forcedfail)
         log_write(0, LOG_MAIN|LOG_PANIC,
           "expansion of \"%s\" (message_id_header_domain) "
           "failed: %s", message_id_domain, expand_string_message);
       }
-    else if (*new_id_domain != 0)
+    else if (*new_id_domain)
       {
       id_domain = new_id_domain;
-      for (p = id_domain; *p != 0; p++)
+      for (p = id_domain; *p; p++)
         if (!isalnum(*p) && *p != '.') *p = '-';  /* No need to test '-' ! */
       }
     }
@@ -2700,21 +2685,20 @@ if (msgid_header == NULL &&
   /* Permit all characters except controls and RFC 2822 specials in the
   additional text part. */
 
-  if (message_id_text != NULL)
+  if (message_id_text)
     {
     uschar *new_id_text = expand_string(message_id_text);
-    if (new_id_text == NULL)
+    if (!new_id_text)
       {
       if (!expand_string_forcedfail)
         log_write(0, LOG_MAIN|LOG_PANIC,
           "expansion of \"%s\" (message_id_header_text) "
           "failed: %s", message_id_text, expand_string_message);
       }
-    else if (*new_id_text != 0)
+    else if (*new_id_text)
       {
       id_text = new_id_text;
-      for (p = id_text; *p != 0; p++)
-        if (mac_iscntrl_or_special(*p)) *p = '-';
+      for (p = id_text; *p; p++) if (mac_iscntrl_or_special(*p)) *p = '-';
       }
     }
 
@@ -2757,9 +2741,8 @@ possible addition of a Sender: header, because untrusted_set_sender allows an
 untrusted user to set anything in the envelope (which might then get info
 From:) but we still want to ensure a valid Sender: if it is required. */
 
-if (from_header == NULL &&
-    ((sender_host_address == NULL && !suppress_local_fixups)
-      || submission_mode))
+if (  !from_header
+   && ((!sender_host_address && !suppress_local_fixups) || submission_mode))
   {
   uschar *oname = US"";
 
@@ -2768,7 +2751,7 @@ if (from_header == NULL &&
   force its value or if we have a non-SMTP message for which -f was not used
   to set the sender. */
 
-  if (sender_host_address == NULL)
+  if (!sender_host_address)
     {
     if (!trusted_caller || sender_name_forced ||
          (!smtp_input && !sender_address_forced))
@@ -2778,46 +2761,38 @@ if (from_header == NULL &&
   /* For non-locally submitted messages, the only time we use the originator
   name is when it was forced by the /name= option on control=submission. */
 
-  else
-    {
-    if (submission_name != NULL) oname = submission_name;
-    }
+  else if (submission_name) oname = submission_name;
 
   /* Envelope sender is empty */
 
-  if (sender_address[0] == 0)
+  if (!*sender_address)
     {
     uschar *fromstart, *fromend;
 
-    fromstart = string_sprintf("%sFrom: %s%s", resent_prefix,
-      oname, (oname[0] == 0)? "" : " <");
-    fromend = (oname[0] == 0)? US"" : US">";
+    fromstart = string_sprintf("%sFrom: %s%s",
+      resent_prefix, oname, *oname ? " <" : "");
+    fromend = *oname ? US">" : US"";
 
     if (sender_local || local_error_message)
-      {
       header_add(htype_from, "%s%s@%s%s\n", fromstart,
         local_part_quote(originator_login), qualify_domain_sender,
         fromend);
-      }
-    else if (submission_mode && authenticated_id != NULL)
+
+    else if (submission_mode && authenticated_id)
       {
-      if (submission_domain == NULL)
-        {
+      if (!submission_domain)
         header_add(htype_from, "%s%s@%s%s\n", fromstart,
           local_part_quote(authenticated_id), qualify_domain_sender,
           fromend);
-        }
-      else if (submission_domain[0] == 0)  /* empty => whole address set */
-        {
+
+      else if (!*submission_domain)  /* empty => whole address set */
         header_add(htype_from, "%s%s%s\n", fromstart, authenticated_id,
           fromend);
-        }
+
       else
-        {
         header_add(htype_from, "%s%s@%s%s\n", fromstart,
-          local_part_quote(authenticated_id), submission_domain,
-          fromend);
-        }
+          local_part_quote(authenticated_id), submission_domain, fromend);
+
       from_header = header_last;    /* To get it checked for Sender: */
       }
     }
@@ -2830,10 +2805,9 @@ if (from_header == NULL &&
     {
     header_add(htype_from, "%sFrom: %s%s%s%s\n", resent_prefix,
       oname,
-      (oname[0] == 0)? "" : " <",
-      (sender_address_unrewritten == NULL)?
-        sender_address : sender_address_unrewritten,
-      (oname[0] == 0)? "" : ">");
+      *oname ? " <" : "",
+      sender_address_unrewritten ? sender_address_unrewritten : sender_address,
+      *oname ? ">" : "");
 
     from_header = header_last;    /* To get it checked for Sender: */
     }
@@ -2850,11 +2824,11 @@ trusted callers to forge From: without supplying -f, we have to test explicitly
 here. If the From: header contains more than one address, then the call to
 parse_extract_address fails, and a Sender: header is inserted, as required. */
 
-if (from_header != NULL &&
-     (active_local_from_check &&
-       ((sender_local && !trusted_caller && !suppress_local_fixups) ||
-        (submission_mode && authenticated_id != NULL))
-     ))
+if (  from_header
+   && (  active_local_from_check
+      && (  sender_local && !trusted_caller && !suppress_local_fixups
+        || submission_mode && authenticated_id
+   )  )  )
   {
   BOOL make_sender = TRUE;
   int start, end, domain;
@@ -2864,37 +2838,26 @@ if (from_header != NULL &&
       &start, &end, &domain, FALSE);
   uschar *generated_sender_address;
 
-  if (submission_mode)
-    {
-    if (submission_domain == NULL)
-      {
-      generated_sender_address = string_sprintf("%s@%s",
-        local_part_quote(authenticated_id), qualify_domain_sender);
-      }
-    else if (submission_domain[0] == 0)  /* empty => full address */
-      {
-      generated_sender_address = string_sprintf("%s",
-        authenticated_id);
-      }
-    else
-      {
-      generated_sender_address = string_sprintf("%s@%s",
-        local_part_quote(authenticated_id), submission_domain);
-      }
-    }
-  else
-    generated_sender_address = string_sprintf("%s@%s",
-      local_part_quote(originator_login), qualify_domain_sender);
+  generated_sender_address = submission_mode
+    ? !submission_domain
+    ? string_sprintf("%s@%s",
+       local_part_quote(authenticated_id), qualify_domain_sender)
+    : !*submission_domain                      /* empty => full address */
+    ? string_sprintf("%s", authenticated_id)
+    : string_sprintf("%s@%s",
+       local_part_quote(authenticated_id), submission_domain)
+    : string_sprintf("%s@%s",
+       local_part_quote(originator_login), qualify_domain_sender);
 
   /* Remove permitted prefixes and suffixes from the local part of the From:
   address before doing the comparison with the generated sender. */
 
-  if (from_address != NULL)
+  if (from_address)
     {
     int slen;
-    uschar *at = (domain == 0)? NULL : from_address + domain - 1;
+    uschar *at = domain ? from_address + domain - 1 : NULL;
 
-    if (at != NULL) *at = 0;
+    if (at) *at = 0;
     from_address += route_check_prefix(from_address, local_from_prefix);
     slen = route_check_suffix(from_address, local_from_suffix);
     if (slen > 0)
@@ -2902,10 +2865,10 @@ if (from_header != NULL &&
       memmove(from_address+slen, from_address, Ustrlen(from_address)-slen);
       from_address += slen;
       }
-    if (at != NULL) *at = '@';
+    if (at) *at = '@';
 
-    if (strcmpic(generated_sender_address, from_address) == 0 ||
-      (domain == 0 && strcmpic(from_address, originator_login) == 0))
+    if (  strcmpic(generated_sender_address, from_address) == 0
+       || (!domain && strcmpic(from_address, originator_login) == 0))
         make_sender = FALSE;
     }
 
@@ -2913,8 +2876,7 @@ if (from_header != NULL &&
   appropriate rewriting rules. */
 
   if (make_sender)
-    {
-    if (submission_mode && submission_name == NULL)
+    if (submission_mode && !submission_name)
       header_add(htype_sender, "%sSender: %s\n", resent_prefix,
         generated_sender_address);
     else
@@ -2922,14 +2884,13 @@ if (from_header != NULL &&
         resent_prefix,
         submission_mode? submission_name : originator_name,
         generated_sender_address);
-    }
 
   /* Ensure that a non-null envelope sender address corresponds to the
   submission mode sender address. */
 
-  if (submission_mode && sender_address[0] != 0)
+  if (submission_mode && *sender_address)
     {
-    if (sender_address_unrewritten == NULL)
+    if (!sender_address_unrewritten)
       sender_address_unrewritten = sender_address;
     sender_address = generated_sender_address;
     if (Ustrcmp(sender_address_unrewritten, generated_sender_address) != 0)
@@ -2942,8 +2903,7 @@ if (from_header != NULL &&
 /* If there are any rewriting rules, apply them to the sender address, unless
 it has already been rewritten as part of verification for SMTP input. */
 
-if (global_rewrite_rules != NULL && sender_address_unrewritten == NULL &&
-    sender_address[0] != 0)
+if (global_rewrite_rules && !sender_address_unrewritten && *sender_address)
   {
   sender_address = rewrite_address(sender_address, FALSE, TRUE,
     global_rewrite_rules, rewrite_existflags);
@@ -2992,9 +2952,8 @@ to be more confusing if Exim adds one to all remotely-originated messages.
 As per Message-Id, we prepend if resending, else append.
 */
 
-if (!date_header_exists &&
-      ((sender_host_address == NULL && !suppress_local_fixups)
-        || submission_mode))
+if (  !date_header_exists
+   && ((!sender_host_address && !suppress_local_fixups) || submission_mode))
   header_add_at_position(!resents_exist, NULL, FALSE, htype_other,
     "%sDate: %s\n", resent_prefix, tod_stamp(tod_full));
 
@@ -3006,7 +2965,7 @@ new Received:) has not yet been set. */
 DEBUG(D_receive)
   {
   debug_printf(">>Headers after rewriting and local additions:\n");
-  for (h = header_list->next; h != NULL; h = h->next)
+  for (h = header_list->next; h; h = h->next)
     debug_printf("%c %s", h->type, h->text);
   debug_printf("\n");
   }
@@ -3108,7 +3067,7 @@ format); write it (remembering that it might contain binary zeros). The result
 of fwrite() isn't inspected; instead we call ferror() below. */
 
 fprintf(data_file, "%s-D\n", message_id);
-if (next != NULL)
+if (next)
   {
   uschar *s = next->text;
   int len = next->slen;
@@ -3163,10 +3122,10 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT)
       log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: "
        "message too big: read=%d max=%d",
        sender_address,
-       (sender_fullhost == NULL)? "" : " H=",
-       (sender_fullhost == NULL)? US"" : sender_fullhost,
-       (sender_ident == NULL)? "" : " U=",
-       (sender_ident == NULL)? US"" : sender_ident,
+       sender_fullhost ? " H=" : "",
+       sender_fullhost ? sender_fullhost : US"",
+       sender_ident ? " U=" : "",
+       sender_ident ? sender_ident : US"",
        message_size,
        thismessage_size_limit);
 
@@ -3219,7 +3178,7 @@ if (fflush(data_file) == EOF || ferror(data_file) ||
   uschar *msg = string_sprintf("%s error (%s) while receiving message from %s",
     input_error? "Input read" : "Spool write",
     msg_errno,
-    (sender_fullhost != NULL)? sender_fullhost : sender_ident);
+    sender_fullhost ? sender_fullhost : sender_ident);
 
   log_write(0, LOG_MAIN, "Message abandoned: %s", msg);
   Uunlink(spool_name);                /* Lose the data file */
@@ -3251,6 +3210,7 @@ if (fflush(data_file) == EOF || ferror(data_file) ||
 /* No I/O errors were encountered while writing the data file. */
 
 DEBUG(D_receive) debug_printf("Data file written for message %s\n", message_id);
+if (LOGGING(receive_time)) timesince(&received_time_taken, &received_time);
 
 
 /* If there were any bad addresses extracted by -t, or there were no recipients
@@ -3264,12 +3224,12 @@ recipients or stderr error writing, throw the data file away afterwards, and
 exit. (This can't be SMTP, which always ensures there's at least one
 syntactically good recipient address.) */
 
-if (extract_recip && (bad_addresses != NULL || recipients_count == 0))
+if (extract_recip && (bad_addresses || recipients_count == 0))
   {
   DEBUG(D_receive)
     {
     if (recipients_count == 0) debug_printf("*** No recipients\n");
-    if (bad_addresses != NULL)
+    if (bad_addresses)
       {
       error_block *eblock = bad_addresses;
       debug_printf("*** Bad address(es)\n");
@@ -3300,7 +3260,7 @@ if (extract_recip && (bad_addresses != NULL || recipients_count == 0))
     }
   else
     {
-    if (bad_addresses == NULL)
+    if (!bad_addresses)
       {
       if (extracted_ignored)
         fprintf(stderr, "exim: all -t recipients overridden by command line\n");
@@ -3345,7 +3305,7 @@ Note: the checking for too many Received: headers is handled by the delivery
 code. */
 /*XXX eventually add excess Received: check for cutthrough case back when classifying them */
 
-if (received_header->text == NULL)     /* Non-cutthrough case */
+if (!received_header->text)    /* Non-cutthrough case */
   {
   received_header_gen();
 
@@ -3388,99 +3348,93 @@ else
 #ifndef DISABLE_DKIM
     if (!dkim_disable_verify)
       {
-      /* Finish verification, this will log individual signature results to
-         the mainlog */
+      /* Finish verification */
       dkim_exim_verify_finish();
 
       /* Check if we must run the DKIM ACL */
       if (acl_smtp_dkim && dkim_verify_signers && *dkim_verify_signers)
         {
-        uschar *dkim_verify_signers_expanded =
+        uschar * dkim_verify_signers_expanded =
           expand_string(dkim_verify_signers);
-        if (!dkim_verify_signers_expanded)
+       gstring * results = NULL;
+       int signer_sep = 0;
+       const uschar * ptr;
+       uschar * item;
+       gstring * seen_items = NULL;
+       int old_pool = store_pool;
+
+       store_pool = POOL_PERM;   /* Allow created variables to live to data ACL */
+
+        if (!(ptr = dkim_verify_signers_expanded))
           log_write(0, LOG_MAIN|LOG_PANIC,
             "expansion of dkim_verify_signers option failed: %s",
             expand_string_message);
 
-        else
-          {
-          int sep = 0;
-          const uschar * ptr = dkim_verify_signers_expanded;
-          uschar * item = NULL;
-          gstring * seen_items = NULL;
-
-          /* Default to OK when no items are present */
-          rc = OK;
-          while ((item = string_nextinlist(&ptr, &sep, NULL, 0)))
-            {
-            /* Prevent running ACL for an empty item */
-            if (!item || !*item) continue;
-
-            /* Only run ACL once for each domain or identity,
-           no matter how often it appears in the expanded list. */
-            if (seen_items)
-              {
-              uschar *seen_item;
-              const uschar *seen_items_list = string_from_gstring(seen_items);
-              BOOL seen_this_item = FALSE;
-
-              while ((seen_item = string_nextinlist(&seen_items_list, &sep,
-                                                    NULL, 0)))
-               if (Ustrcmp(seen_item,item) == 0)
-                 {
-                 seen_this_item = TRUE;
-                 break;
-                 }
-
-              if (seen_this_item)
-                {
-                DEBUG(D_receive)
-                  debug_printf("acl_smtp_dkim: skipping signer %s, "
-                   "already seen\n", item);
-                continue;
-                }
-
-              seen_items = string_cat(seen_items, ":");
-              }
-
-            seen_items = string_cat(seen_items, item);
-
-            DEBUG(D_receive)
-              debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n",
-               item);
-
-            dkim_exim_acl_setup(item);
-            rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim,
-                 &user_msg, &log_msg);
-           dkim_exim_verify_log_item();
-
-            if (rc != OK)
+       /* Default to OK when no items are present */
+       rc = OK;
+       while ((item = string_nextinlist(&ptr, &signer_sep, NULL, 0)))
+         {
+         /* Prevent running ACL for an empty item */
+         if (!item || !*item) continue;
+
+         /* Only run ACL once for each domain or identity,
+         no matter how often it appears in the expanded list. */
+         if (seen_items)
+           {
+           uschar * seen_item;
+           const uschar * seen_items_list = string_from_gstring(seen_items);
+           int seen_sep = ':';
+           BOOL seen_this_item = FALSE;
+
+           while ((seen_item = string_nextinlist(&seen_items_list, &seen_sep,
+                                                 NULL, 0)))
+             if (Ustrcmp(seen_item,item) == 0)
+               {
+               seen_this_item = TRUE;
+               break;
+               }
+
+           if (seen_this_item)
              {
              DEBUG(D_receive)
-               debug_printf("acl_smtp_dkim: acl_check returned %d on %s, "
-                 "skipping remaining items\n", rc, item);
-             cancel_cutthrough_connection(TRUE, US"dkim acl not ok");
-             break;
+               debug_printf("acl_smtp_dkim: skipping signer %s, "
+                 "already seen\n", item);
+             continue;
              }
-            }
-          add_acl_headers(ACL_WHERE_DKIM, US"DKIM");
-          if (rc == DISCARD)
-            {
-            recipients_count = 0;
-            blackholed_by = US"DKIM ACL";
-            if (log_msg)
-              blackhole_log_msg = string_sprintf(": %s", log_msg);
-            }
-          else if (rc != OK)
-            {
-            Uunlink(spool_name);
-            if (smtp_handle_acl_fail(ACL_WHERE_DKIM, rc, user_msg, log_msg) != 0)
-              smtp_yield = FALSE;    /* No more messages after dropped connection */
-            smtp_reply = US"";       /* Indicate reply already sent */
-            message_id[0] = 0;       /* Indicate no message accepted */
-            goto TIDYUP;             /* Skip to end of function */
-            }
-          }
+
+           seen_items = string_catn(seen_items, US":", 1);
+           }
+         seen_items = string_cat(seen_items, item);
+
+         rc = dkim_exim_acl_run(item, &results, &user_msg, &log_msg);
+         if (rc != OK)
+           {
+           DEBUG(D_receive)
+             debug_printf("acl_smtp_dkim: acl_check returned %d on %s, "
+               "skipping remaining items\n", rc, item);
+           cancel_cutthrough_connection(TRUE, US"dkim acl not ok");
+           break;
+           }
+         }
+       dkim_verify_status = string_from_gstring(results);
+       store_pool = old_pool;
+       add_acl_headers(ACL_WHERE_DKIM, US"DKIM");
+       if (rc == DISCARD)
+         {
+         recipients_count = 0;
+         blackholed_by = US"DKIM ACL";
+         if (log_msg)
+           blackhole_log_msg = string_sprintf(": %s", log_msg);
+         }
+       else if (rc != OK)
+         {
+         Uunlink(spool_name);
+         if (smtp_handle_acl_fail(ACL_WHERE_DKIM, rc, user_msg, log_msg) != 0)
+           smtp_yield = FALSE;    /* No more messages after dropped connection */
+         smtp_reply = US"";       /* Indicate reply already sent */
+         message_id[0] = 0;       /* Indicate no message accepted */
+         goto TIDYUP;             /* Skip to end of function */
+         }
         }
       else
        dkim_exim_verify_log_all();
@@ -3488,9 +3442,10 @@ else
 #endif /* DISABLE_DKIM */
 
 #ifdef WITH_CONTENT_SCAN
-    if (recipients_count > 0 &&
-        acl_smtp_mime != NULL &&
-        !run_mime_acl(acl_smtp_mime, &smtp_yield, &smtp_reply, &blackholed_by))
+    if (  recipients_count > 0
+       && acl_smtp_mime
+       && !run_mime_acl(acl_smtp_mime, &smtp_yield, &smtp_reply, &blackholed_by)
+       )
       goto TIDYUP;
 #endif /* WITH_CONTENT_SCAN */
 
@@ -3610,21 +3565,23 @@ else
     {
 
 #ifdef WITH_CONTENT_SCAN
-    if (acl_not_smtp_mime != NULL &&
-        !run_mime_acl(acl_not_smtp_mime, &smtp_yield, &smtp_reply,
-          &blackholed_by))
+    if (  acl_not_smtp_mime
+       && !run_mime_acl(acl_not_smtp_mime, &smtp_yield, &smtp_reply,
+          &blackholed_by)
+       )
       goto TIDYUP;
 #endif /* WITH_CONTENT_SCAN */
 
-    if (acl_not_smtp != NULL)
+    if (acl_not_smtp)
       {
       uschar *user_msg, *log_msg;
+      authentication_local = TRUE;
       rc = acl_check(ACL_WHERE_NOTSMTP, NULL, acl_not_smtp, &user_msg, &log_msg);
       if (rc == DISCARD)
         {
         recipients_count = 0;
         blackholed_by = US"non-SMTP ACL";
-        if (log_msg != NULL)
+        if (log_msg)
           blackhole_log_msg = string_sprintf(": %s", log_msg);
         }
       else if (rc != OK)
@@ -3639,11 +3596,11 @@ else
         /* The ACL can specify where rejections are to be logged, possibly
         nowhere. The default is main and reject logs. */
 
-        if (log_reject_target != 0)
+        if (log_reject_target)
           log_write(0, log_reject_target, "F=<%s> rejected by non-SMTP ACL: %s",
             sender_address, log_msg);
 
-        if (user_msg == NULL) user_msg = US"local configuration problem";
+        if (!user_msg) user_msg = US"local configuration problem";
         if (smtp_batched_input)
           {
           moan_smtp_batch(NULL, "%d %s", 550, user_msg);
@@ -3780,32 +3737,32 @@ else
   switch(rc)
     {
     default:
-    log_write(0, LOG_MAIN, "invalid return %d from local_scan(). Temporary "
-      "rejection given", rc);
-    goto TEMPREJECT;
+      log_write(0, LOG_MAIN, "invalid return %d from local_scan(). Temporary "
+       "rejection given", rc);
+      goto TEMPREJECT;
 
     case LOCAL_SCAN_REJECT_NOLOGHDR:
-    BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header);
-    /* Fall through */
+      BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header);
+      /* Fall through */
 
     case LOCAL_SCAN_REJECT:
-    smtp_code = US"550";
-    if (errmsg == NULL) errmsg =  US"Administrative prohibition";
-    break;
+      smtp_code = US"550";
+      if (!errmsg) errmsg =  US"Administrative prohibition";
+      break;
 
     case LOCAL_SCAN_TEMPREJECT_NOLOGHDR:
-    BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header);
-    /* Fall through */
+      BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header);
+      /* Fall through */
 
     case LOCAL_SCAN_TEMPREJECT:
     TEMPREJECT:
-    smtp_code = US"451";
-    if (errmsg == NULL) errmsg = US"Temporary local problem";
-    istemp = US"temporarily ";
-    break;
+      smtp_code = US"451";
+      if (!errmsg) errmsg = US"Temporary local problem";
+      istemp = US"temporarily ";
+      break;
     }
 
-  g = string_append(g, 2, US"F=",
+  g = string_append(NULL, 2, US"F=",
     sender_address[0] == 0 ? US"<>" : sender_address);
   g = add_host_info_for_log(g);
 
@@ -4002,6 +3959,18 @@ if (LOGGING(8bitmime))
   g = string_append(g, 2, US" M8S=", big_buffer);
   }
 
+#ifndef DISABLE_DKIM
+if (LOGGING(dkim) && dkim_verify_overall)
+  g = string_append(g, 2, US" DKIM=", dkim_verify_overall);
+# ifdef EXPERIMENTAL_ARC
+if (LOGGING(dkim) && arc_state && Ustrcmp(arc_state, "pass") == 0)
+  g = string_catn(g, US" ARC", 4);
+# endif
+#endif
+
+if (LOGGING(receive_time))
+  g = string_append(g, 2, US" RT=", string_timediff(&received_time_taken));
+
 if (*queue_name)
   g = string_append(g, 2, US" Q=", queue_name);
 
@@ -4025,7 +3994,7 @@ if (msgid_header)
 /* If subject logging is turned on, create suitable printing-character
 text. By expanding $h_subject: we make use of the MIME decoding. */
 
-if (LOGGING(subject) && subject_header != NULL)
+if (LOGGING(subject) && subject_header)
   {
   int i;
   uschar *p = big_buffer;
@@ -4188,9 +4157,11 @@ if(cutthrough.fd >= 0 && cutthrough.delivery)
     case '4':  /* Temp-reject. Keep spoolfiles and accept, unless defer-pass mode.
                ... for which, pass back the exact error */
       if (cutthrough.defer_pass) smtp_reply = string_copy_malloc(msg);
-      /*FALLTRHOUGH*/
+      cutthrough_done = TMP_REJ;               /* Avoid the usual immediate delivery attempt */
+      break;                                   /* message_id needed for SMTP accept below */
 
     default:   /* Unknown response, or error.  Treat as temp-reject.         */
+      if (cutthrough.defer_pass) smtp_reply = US"450 Onward transmission not accepted";
       cutthrough_done = TMP_REJ;               /* Avoid the usual immediate delivery attempt */
       break;                                   /* message_id needed for SMTP accept below */