Fix 2-phase, in-order queue run delivery order
[exim.git] / src / src / transport.c
index 1e8bb4aa70b5d5377d3f4a1bd5e3941c94928c7c..658fc62353e32574f6214b786ab6164bd0367418 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) The Exim Maintainers 2020 - 2022 */
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 /* SPDX-License-Identifier: GPL-2.0-or-later */
@@ -434,10 +434,10 @@ Returns:     TRUE on success, FALSE on failure (with errno preserved)
 */
 
 BOOL
 */
 
 BOOL
-write_chunk(transport_ctx * tctx, uschar *chunk, int len)
+write_chunk(transport_ctx * tctx, const uschar * chunk, int len)
 {
 {
-uschar *start = chunk;
-uschar *end = chunk + len;
+const uschar * start = chunk;
+const uschar * end = chunk + len;
 int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2;
 
 /* The assumption is made that the check string will never stretch over move
 int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2;
 
 /* The assumption is made that the check string will never stretch over move
@@ -474,7 +474,7 @@ if (nl_partial_match >= 0)
 for possible escaping. The code for the non-NL route should be as fast as
 possible. */
 
 for possible escaping. The code for the non-NL route should be as fast as
 possible. */
 
-for (uschar * ptr = start; ptr < end; ptr++)
+for (const uschar * ptr = start; ptr < end; ptr++)
   {
   int ch, len;
 
   {
   int ch, len;
 
@@ -580,7 +580,7 @@ Arguments:
 Returns:            a string
 */
 
 Returns:            a string
 */
 
-uschar *
+const uschar *
 transport_rcpt_address(address_item *addr, BOOL include_affixes)
 {
 uschar *at;
 transport_rcpt_address(address_item *addr, BOOL include_affixes)
 {
 uschar *at;
@@ -704,7 +704,7 @@ Returns:                TRUE on success; FALSE on failure.
 */
 BOOL
 transport_headers_send(transport_ctx * tctx,
 */
 BOOL
 transport_headers_send(transport_ctx * tctx,
-  BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len))
+  BOOL (*sendfn)(transport_ctx * tctx, const uschar * s, int len))
 {
 const uschar * list;
 transport_instance * tblock = tctx ? tctx->tblock : NULL;
 {
 const uschar * list;
 transport_instance * tblock = tctx ? tctx->tblock : NULL;
@@ -1506,7 +1506,7 @@ open_db *dbm_file;
 if (!is_new_message_id(message_id))
   {
   DEBUG(D_transport) debug_printf("message_id %s is not new format; "
 if (!is_new_message_id(message_id))
   {
   DEBUG(D_transport) debug_printf("message_id %s is not new format; "
-    "skipping wait-%s database update\n", tpname);
+    "skipping wait-%s database update\n", message_id, tpname);
   return;
   }
 
   return;
   }
 
@@ -1658,6 +1658,10 @@ another message waiting for the same host. However, it doesn't do this if the
 current continue sequence is greater than the maximum supplied as an argument,
 or greater than the global connection_max_messages, which, if set, overrides.
 
 current continue sequence is greater than the maximum supplied as an argument,
 or greater than the global connection_max_messages, which, if set, overrides.
 
+It is also called if conditions are otherwise right for pipelining a QUIT after
+the message data, since if there is another message waiting we do not want to
+send that QUIT.
+
 Arguments:
   transport_name     name of the transport
   hostname           name of the host
 Arguments:
   transport_name     name of the transport
   hostname           name of the host
@@ -1698,7 +1702,7 @@ DEBUG(D_transport)
   acl_level++;
   }
 
   acl_level++;
   }
 
-/* Do nothing if we have hit the maximum number that can be send down one
+/* Do nothing if we have hit the maximum number that can be sent down one
 connection. */
 
 if (connection_max_messages >= 0) local_message_max = connection_max_messages;
 connection. */
 
 if (connection_max_messages >= 0) local_message_max = connection_max_messages;
@@ -1735,7 +1739,7 @@ if (host_record->count > WAIT_NAME_MAX)
   goto retfalse;
   }
 
   goto retfalse;
   }
 
-/* Scan the message ids in the record from the end towards the beginning,
+/* Scan the message ids in the record in order
 until one is found for which a spool file actually exists. If the record gets
 emptied, delete it and continue with any continuation records that may exist.
 */
 until one is found for which a spool file actually exists. If the record gets
 emptied, delete it and continue with any continuation records that may exist.
 */
@@ -1748,17 +1752,14 @@ host_length = host_record->count * MESSAGE_ID_LENGTH;
 
 while (1)
   {
 
 while (1)
   {
-  msgq_t      *msgq;
-  int         msgq_count = 0;
-  int         msgq_actual = 0;
-  BOOL        bFound = FALSE;
-  BOOL        bContinuation = FALSE;
+  msgq_t * msgq;
+  int msgq_count = 0, msgq_actual = 0;
+  BOOL bFound = FALSE, bContinuation = FALSE;
 
   /* create an array to read entire message queue into memory for processing  */
 
   msgq = store_get(sizeof(msgq_t) * host_record->count, GET_UNTAINTED);
 
   /* create an array to read entire message queue into memory for processing  */
 
   msgq = store_get(sizeof(msgq_t) * host_record->count, GET_UNTAINTED);
-  msgq_count = host_record->count;
-  msgq_actual = msgq_count;
+  msgq_actual = msgq_count = host_record->count;
 
   for (i = 0; i < host_record->count; ++i)
     {
 
   for (i = 0; i < host_record->count; ++i)
     {
@@ -1772,10 +1773,9 @@ while (1)
        debug_printf_indent("NOTE: old or corrupt message-id found in wait=%.200s"
          " hints DB; deleting records for %s\n", transport_name, hostname);
       (void) dbfn_delete(dbm_file, hostname);
        debug_printf_indent("NOTE: old or corrupt message-id found in wait=%.200s"
          " hints DB; deleting records for %s\n", transport_name, hostname);
       (void) dbfn_delete(dbm_file, hostname);
-      for (int i = host_record->sequence - 1; i >= 0; i--)
+      for (int j = host_record->sequence - 1; j >= 0; j--)
        (void) dbfn_delete(dbm_file,
        (void) dbfn_delete(dbm_file,
-                   (sprintf(CS buffer, "%.200s:%d", hostname, i), buffer));
-      dbfn_close(dbm_file);
+                   (sprintf(CS buffer, "%.200s:%d", hostname, j), buffer));
       goto retfalse;
       }
     msgq[i].bKeep = TRUE;
       goto retfalse;
       }
     msgq[i].bKeep = TRUE;
@@ -1797,7 +1797,7 @@ while (1)
 
   /* now find the next acceptable message_id */
 
 
   /* now find the next acceptable message_id */
 
-  for (i = msgq_count - 1; i >= 0; --i) if (msgq[i].bKeep)
+  for (i = 0; i < msgq_count; i++) if (msgq[i].bKeep)
     {
     uschar subdir[2];
     uschar * mid = msgq[i].message_id;
     {
     uschar subdir[2];
     uschar * mid = msgq[i].message_id;
@@ -1891,8 +1891,8 @@ while (1)
     }
 
   /* we were not able to find an acceptable message, nor was there a
     }
 
   /* we were not able to find an acceptable message, nor was there a
-   * continuation record.  So bug out, outer logic will clean this up.
-   */
+  continuation record.  So bug out, outer logic will clean this up.
+  */
 
   if (!bContinuation)
     {
 
   if (!bContinuation)
     {
@@ -1914,7 +1914,11 @@ if (host_length > 0)
   }
 
 dbfn_close(dbm_file);
   }
 
 dbfn_close(dbm_file);
-DEBUG(D_transport) {acl_level--; debug_printf("transport_check_waiting: TRUE\n"); }
+DEBUG(D_transport)
+  {
+  acl_level--;
+  debug_printf("transport_check_waiting: TRUE (found %s)\n", new_message_id);
+  }
 return TRUE;
 
 retfalse:
 return TRUE;
 
 retfalse:
@@ -1937,7 +1941,7 @@ const uschar **argv;
 #ifndef DISABLE_TLS
 if (smtp_peer_options & OPTION_TLS) i += 6;
 #endif
 #ifndef DISABLE_TLS
 if (smtp_peer_options & OPTION_TLS) i += 6;
 #endif
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
 if (continue_limit_mail || continue_limit_rcpt || continue_limit_rcptdom)
                                    i += 4;
 #endif
 if (continue_limit_mail || continue_limit_rcpt || continue_limit_rcptdom)
                                    i += 4;
 #endif
@@ -1979,7 +1983,7 @@ if (smtp_peer_options & OPTION_TLS)
     argv[i++] = US"-MCT";
 #endif
 
     argv[i++] = US"-MCT";
 #endif
 
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
 if (continue_limit_rcpt || continue_limit_rcptdom)
   {
   argv[i++] = US"-MCL";
 if (continue_limit_rcpt || continue_limit_rcptdom)
   {
   argv[i++] = US"-MCL";
@@ -2051,7 +2055,7 @@ Returns:          FALSE if fork fails; TRUE otherwise
 BOOL
 transport_pass_socket(const uschar *transport_name, const uschar *hostname,
   const uschar *hostaddress, uschar *id, int socket_fd
 BOOL
 transport_pass_socket(const uschar *transport_name, const uschar *hostname,
   const uschar *hostaddress, uschar *id, int socket_fd
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
   , unsigned peer_limit_mail, unsigned peer_limit_rcpt, unsigned peer_limit_rcptdom
 #endif
   )
   , unsigned peer_limit_mail, unsigned peer_limit_rcpt, unsigned peer_limit_rcptdom
 #endif
   )
@@ -2061,7 +2065,7 @@ int status;
 
 DEBUG(D_transport) debug_printf("transport_pass_socket entered\n");
 
 
 DEBUG(D_transport) debug_printf("transport_pass_socket entered\n");
 
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
 continue_limit_mail = peer_limit_mail;
 continue_limit_rcpt = peer_limit_rcpt;
 continue_limit_rcptdom = peer_limit_rcptdom;
 continue_limit_mail = peer_limit_mail;
 continue_limit_rcpt = peer_limit_rcpt;
 continue_limit_rcptdom = peer_limit_rcptdom;
@@ -2302,7 +2306,7 @@ if (flags & TSUC_EXPAND_ARGS)
       address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *), GET_UNTAINTED);
 
       /* +1 because addr->local_part[0] == '|' since af_force_command is set */
       address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *), GET_UNTAINTED);
 
       /* +1 because addr->local_part[0] == '|' since af_force_command is set */
-      s = expand_string(addr->local_part + 1);
+      s = expand_cstring(addr->local_part + 1);
 
       if (!s || !*s)
         {
 
       if (!s || !*s)
         {