typoes
[exim.git] / src / src / deliver.c
index a1d16ecedb815d4b186c09f7e9c9d37aa3db22c6..9066a14becd8b13c0a35d5a7d40882f9225055c9 100644 (file)
@@ -703,7 +703,7 @@ if (LOGGING(incoming_interface) && LOGGING(outgoing_interface)
   s = LOGGING(outgoing_port)
     ? string_append(s, sizep, ptrp, 2, US"]:",
        string_sprintf("%d", sending_port))
-    : string_cat(s, sizep, ptrp, "]", 1);
+    : string_cat(s, sizep, ptrp, US"]", 1);
   }
 return s;
 }
@@ -718,11 +718,24 @@ s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
 if (LOGGING(outgoing_port))
   s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
     addr->host_used->port));
+
+#ifdef SUPPORT_SOCKS
+if (LOGGING(proxy) && proxy_local_address)
+  {
+  s = string_append(s, sizep, ptrp, 3, US" PRX=[", proxy_local_address, US"]");
+  if (LOGGING(outgoing_port))
+    s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
+      proxy_local_port));
+  }
+#endif
+
 return d_log_interface(s, sizep, ptrp);
 }
 
 
 
+
+
 #ifdef SUPPORT_TLS
 static uschar *
 d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
@@ -750,7 +763,7 @@ return s;
 
 
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
 uschar *
 event_raise(uschar * action, const uschar * event, uschar * ev_data)
 {
@@ -785,12 +798,14 @@ if (action)
 return NULL;
 }
 
-static void
+void
 msg_event_raise(const uschar * event, const address_item * addr)
 {
 const uschar * save_domain = deliver_domain;
 uschar * save_local =  deliver_localpart;
 const uschar * save_host = deliver_host;
+const uschar * save_address = deliver_host_address;
+const int      save_port =   deliver_host_port;
 
 if (!addr->transport)
   return;
@@ -802,15 +817,19 @@ deliver_localpart = addr->local_part;
 deliver_host =   addr->host_used ? addr->host_used->name : NULL;
 
 (void) event_raise(addr->transport->event_action, event,
-         addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0
-         ? addr->message : NULL);
+         addr->host_used
+          || Ustrcmp(addr->transport->driver_name, "smtp") == 0
+         || Ustrcmp(addr->transport->driver_name, "lmtp") == 0
+        ? addr->message : NULL); 
 
+deliver_host_port =    save_port;
+deliver_host_address = save_address;
 deliver_host =      save_host;
 deliver_localpart = save_local;
 deliver_domain =    save_domain;
 router_name = transport_name = NULL;
 }
-#endif /*EXPERIMENTAL_EVENT*/
+#endif /*DISABLE_EVENT*/
 
 
 
@@ -835,7 +854,7 @@ the log line, and reset the store afterwards. Remote deliveries should always
 have a pointer to the host item that succeeded; local deliveries can have a
 pointer to a single host item in their host list, for use by the transport. */
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
   /* presume no successful remote delivery */
   lookup_dnssec_authenticated = NULL;
 #endif
@@ -853,7 +872,7 @@ else
 
 if (LOGGING(sender_on_delivery) || msg)
   s = string_append(s, &size, &ptr, 3, US" F=<",
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
     testflag(addr, af_utf8_downcvt)
     ? string_address_utf8_to_alabel(sender_address, NULL)
     :
@@ -909,7 +928,7 @@ else
     if (continue_sequence > 1)
       s = string_cat(s, &size, &ptr, US"*", 1);
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
     deliver_host_address = addr->host_used->address;
     deliver_host_port =    addr->host_used->port;
     deliver_host =         addr->host_used->name;
@@ -980,7 +999,7 @@ store we used to build the line after writing it. */
 s[ptr] = 0;
 log_write(0, flags, "%s", s);
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
 if (!msg) msg_event_raise(US"msg:delivery", addr);
 #endif
 
@@ -1066,9 +1085,7 @@ if (addr->message)
        )
      && (  Ustrstr(s, "mysql")   != NULL
         || Ustrstr(s, "pgsql")   != NULL
-#ifdef EXPERIMENTAL_REDIS
        || Ustrstr(s, "redis")   != NULL
-#endif
        || Ustrstr(s, "sqlite")  != NULL
        || Ustrstr(s, "ldap:")   != NULL
        || Ustrstr(s, "ldapdn:") != NULL
@@ -1288,9 +1305,17 @@ else if (result == DEFER || result == PANIC)
         US strerror(addr->basic_errno));
 
     if (addr->host_used)
+      {
       s = string_append(s, &size, &ptr, 5,
                        US" H=", addr->host_used->name,
                        US" [",  addr->host_used->address, US"]");
+      if (LOGGING(outgoing_port))
+       {
+       int port = addr->host_used->port;
+       s = string_append(s, &size, &ptr, 2,
+             US":", port == PORT_NONE ? US"25" : string_sprintf("%d", port));
+       }
+      }
 
     if (addr->message)
       s = string_append(s, &size, &ptr, 2, US": ", addr->message);
@@ -1412,7 +1437,7 @@ else
 
   log_write(0, LOG_MAIN, "** %s", s);
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
   msg_event_raise(US"msg:fail:delivery", addr);
 #endif
 
@@ -1929,7 +1954,7 @@ address. This feature is not available for shadow transports. */
 
 if (  !shadowing
    && (  tp->return_output || tp->return_fail_output
-      || tp->log_output || tp->log_fail_output
+      || tp->log_output || tp->log_fail_output || tp->log_defer_output
    )  )
   {
   uschar *error;
@@ -1945,9 +1970,6 @@ if (  !shadowing
     }
   }
 
-/*XXX prefer to do max_parallel check before we fork. Are we allowed to defer
-this late (we could be a shadow tpt)? */
-
 /* Create the pipe for inter-process communication. */
 
 if (pipe(pfd) != 0)
@@ -2317,6 +2339,52 @@ if (addr->special_action == SPECIAL_WARN && addr->transport->warn_message)
 
 
 
+
+/* Check transport for the given concurrency limit.  Return TRUE if over
+the limit (or an expansion failure), else FALSE and if there was a limit,
+the key for the hints database used for the concurrency count. */
+
+static BOOL
+tpt_parallel_check(transport_instance * tp, address_item * addr, uschar ** key)
+{
+unsigned max_parallel;
+
+if (!tp->max_parallel) return FALSE;
+
+max_parallel = (unsigned) expand_string_integer(tp->max_parallel, TRUE);
+if (expand_string_message)
+  {
+  log_write(0, LOG_MAIN|LOG_PANIC, "Failed to expand max_parallel option "
+       "in %s transport (%s): %s", tp->name, addr->address,
+       expand_string_message);
+  return TRUE;
+  }
+
+if (max_parallel > 0)
+  {
+  uschar * serialize_key = string_sprintf("tpt-serialize-%s", tp->name);
+  if (!enq_start(serialize_key, max_parallel))
+    {
+    address_item * next;
+    DEBUG(D_transport)
+      debug_printf("skipping tpt %s because concurrency limit %u reached\n",
+                 tp->name, max_parallel);
+    do
+      {
+      next = addr->next;
+      addr->message = US"concurrency limit reached for transport";
+      addr->basic_errno = ERRNO_TRETRY;
+      post_process_one(addr, DEFER, LOG_MAIN, DTYPE_TRANSPORT, 0);
+      } while ((addr = next));
+    return TRUE;
+    }
+  *key = serialize_key;
+  }
+return FALSE;
+}
+
+
+
 /*************************************************
 *              Do local deliveries               *
 *************************************************/
@@ -2348,6 +2416,7 @@ while (addr_local)
   int logflags = LOG_MAIN;
   int logchar = dont_deliver? '*' : '=';
   transport_instance *tp;
+  uschar * serialize_key = NULL;
 
   /* Pick the first undelivered address off the chain */
 
@@ -2483,7 +2552,7 @@ while (addr_local)
         last = next;
         batch_count++;
         }
-      else anchor = &(next->next);      /* Skip the address */
+      else anchor = &next->next;        /* Skip the address */
       }
     }
 
@@ -2614,6 +2683,25 @@ while (addr_local)
 
   if (!addr) continue;
 
+  /* If the transport is limited for parallellism, enforce that here.
+  We use a hints DB entry, incremented here and decremented after
+  the transport (and any shadow transport) completes. */
+
+  if (tpt_parallel_check(tp, addr, &serialize_key))
+    {
+    if (expand_string_message)
+      {
+      logflags |= LOG_PANIC;
+      do
+       {
+       addr = addr->next;
+       post_process_one(addr, DEFER, logflags, DTYPE_TRANSPORT, 0);
+       } while ((addr = addr2));
+      }
+    continue;                  /* Loop for the next set of addresses. */
+    }
+
+
   /* So, finally, we do have some addresses that can be passed to the
   transport. Before doing so, set up variables that are relevant to a
   single delivery. */
@@ -2719,6 +2807,10 @@ while (addr_local)
 
   deliver_set_expansions(NULL);
 
+  /* If the transport was parallelism-limited, decrement the hints DB record. */
+
+  if (serialize_key) enq_end(serialize_key);
+
   /* Now we can process the results of the real transport. We must take each
   address off the chain first, because post_process_one() puts it on another
   chain. */
@@ -2993,7 +3085,7 @@ while (!done)
 
   /* copy and read header */
   memcpy(header, ptr, PIPE_HEADER_SIZE);
-  header[PIPE_HEADER_SIZE] = '\0'; 
+  header[PIPE_HEADER_SIZE] = '\0';
   id = header[0];
   subid = header[1];
   required = Ustrtol(header + 2, &endc, 10) + PIPE_HEADER_SIZE;     /* header + data */
@@ -3006,7 +3098,7 @@ while (!done)
     }
 
   DEBUG(D_deliver)
-    debug_printf("header read  id:%c,subid:%c,size:%s,required:%d,remaining:%d,unfinished:%d\n", 
+    debug_printf("header read  id:%c,subid:%c,size:%s,required:%d,remaining:%d,unfinished:%d\n",
                     id, subid, header+2, required, remaining, unfinished);
 
   /* is there room for the dataset we want to read ? */
@@ -3019,16 +3111,16 @@ while (!done)
     break;
     }
 
-  /* we wrote all datasets with atomic write() calls 
+  /* we wrote all datasets with atomic write() calls
      remaining < required only happens if big_buffer was too small
-     to get all available data from pipe. unfinished has to be true 
+     to get all available data from pipe. unfinished has to be true
      as well. */
   if (remaining < required)
     {
     if (unfinished)
       continue;
     msg = string_sprintf("failed to read pipe from transport process "
-      "%d for transport %s: required size=%d > remaining size=%d and unfinished=false", 
+      "%d for transport %s: required size=%d > remaining size=%d and unfinished=false",
       pid, addr->transport->driver_name, required, remaining);
     done = TRUE;
     break;
@@ -3036,7 +3128,7 @@ while (!done)
 
   /* step behind the header */
   ptr += PIPE_HEADER_SIZE;
+
   /* Handle each possible type of item, assuming the complete item is
   available in store. */
 
@@ -3216,6 +3308,21 @@ while (!done)
 
     switch (subid)
       {
+#ifdef SUPPORT_SOCKS
+      case '2':        /* proxy information; must arrive before A0 and applies to that addr XXX oops*/
+       proxy_session = TRUE;   /*XXX shouod this be cleared somewhere? */
+       if (*ptr == 0)
+         ptr++;
+       else
+         {
+         proxy_local_address = string_copy(ptr);
+         while(*ptr++);
+         memcpy(&proxy_local_port, ptr, sizeof(proxy_local_port));
+         ptr += sizeof(proxy_local_port);
+         }
+       break;
+#endif
+
 #ifdef EXPERIMENTAL_DSN_INFO
       case '1':        /* must arrive before A0, and applies to that addr */
                /* Two strings: smtp_greeting and helo_response */
@@ -3730,7 +3837,14 @@ while (parcount > max)
       "remote delivery process count got out of step");
     parcount = 0;
     }
-  else remote_post_process(doneaddr, LOG_MAIN, NULL, fallback);
+  else
+    {
+    transport_instance * tp = doneaddr->transport;
+    if (tp->max_parallel)
+      enq_end(string_sprintf("tpt-serialize-%s", tp->name));
+
+    remote_post_process(doneaddr, LOG_MAIN, NULL, fallback);
+    }
   }
 }
 
@@ -3748,7 +3862,7 @@ int     header_length;
 
 if (size > 99999)
   {
-  log_write(0, LOG_MAIN|LOG_PANIC_DIE, 
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
     "Failed writing transport result to pipe: can't handle buffers > 99999 bytes. truncating!\n");
   size = 99999;
   }
@@ -3764,7 +3878,7 @@ if (header_length != PIPE_HEADER_SIZE)
   writebuffer[0] = '\0';
   }
 
-DEBUG(D_deliver) debug_printf("header write id:%c,subid:%c,size:%d,final:%s\n", 
+DEBUG(D_deliver) debug_printf("header write id:%c,subid:%c,size:%d,final:%s\n",
                                  id, subid, size, writebuffer);
 
 if (buf && size > 0)
@@ -3853,6 +3967,7 @@ for (delivery_count = 0; addr_remote; delivery_count++)
   address_item *last = addr;
   address_item *next;
   uschar * panicmsg;
+  uschar * serialize_key = NULL;
 
   /* Pull the first address right off the list. */
 
@@ -4027,6 +4142,16 @@ for (delivery_count = 0; addr_remote; delivery_count++)
     return FALSE;
     }
 
+  /* If the transport is limited for parallellism, enforce that here.
+  The hints DB entry is decremented in par_reduce(), when we reap the
+  transport process. */
+
+  if (tpt_parallel_check(tp, addr, &serialize_key))
+    if ((panicmsg = expand_string_message))
+      goto panic_continue;
+    else
+      continue;                        /* Loop for the next set of addresses. */
+
   /* Set up the expansion variables for this set of addresses */
 
   deliver_set_expansions(addr);
@@ -4055,7 +4180,7 @@ for (delivery_count = 0; addr_remote; delivery_count++)
       {
       panicmsg = string_sprintf("Failed to expand return path \"%s\": %s",
        tp->return_path, expand_string_message);
-      goto panic_continue;
+      goto enq_continue;
       }
     }
 
@@ -4066,7 +4191,7 @@ for (delivery_count = 0; addr_remote; delivery_count++)
   if (!findugid(addr, tp, &uid, &gid, &use_initgroups))
     {
     panicmsg = NULL;
-    goto panic_continue;
+    goto enq_continue;
     }
 
   /* If this transport has a setup function, call it now so that it gets
@@ -4104,11 +4229,11 @@ for (delivery_count = 0; addr_remote; delivery_count++)
     if (!ok)
       {
       DEBUG(D_deliver) debug_printf("not suitable for continue_transport\n");
-      next = addr;
+      if (serialize_key) enq_end(serialize_key);
 
       if (addr->fallback_hosts && !fallback)
         {
-        for (;; next = next->next)
+       for (next = addr; ; next = next->next)
           {
           next->host_list = next->fallback_hosts;
           DEBUG(D_deliver) debug_printf("%s queued for fallback host(s)\n", next->address);
@@ -4119,11 +4244,11 @@ for (delivery_count = 0; addr_remote; delivery_count++)
         }
 
       else
-        {
-        while (next->next) next = next->next;
-        next->next = addr_defer;
-        addr_defer = addr;
-        }
+       {
+       while (next->next) next = next->next;
+       next->next = addr_defer;
+       addr_defer = addr;
+       }
 
       continue;
       }
@@ -4185,7 +4310,7 @@ for (delivery_count = 0; addr_remote; delivery_count++)
   if (!pipe_done)
     {
     panicmsg = string_sprintf("unable to create pipe: %s", strerror(errno));
-    goto panic_continue;
+    goto enq_continue;
     }
 
   /* Find a free slot in the pardata list. Must do this after the possible
@@ -4203,7 +4328,7 @@ for (delivery_count = 0; addr_remote; delivery_count++)
     (void)close(pfd[pipe_write]);
     (void)close(pfd[pipe_read]);
     panicmsg = US"Unexpectedly no free subprocess slot";
-    goto panic_continue;
+    goto enq_continue;
     }
 
   /* Now fork a subprocess to do the remote delivery, but before doing so,
@@ -4342,15 +4467,13 @@ for (delivery_count = 0; addr_remote; delivery_count++)
 #ifdef SUPPORT_TLS
       if (addr->cipher)
         {
-        ptr = big_buffer;
-        sprintf(CS ptr, "%.128s", addr->cipher);
-        while(*ptr++);
+        ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->cipher) + 1;
         if (!addr->peerdn)
          *ptr++ = 0;
        else
           {
-          sprintf(CS ptr, "%.512s", addr->peerdn);
-          while(*ptr++);
+          ptr += sprintf(CS ptr, "%.512s", addr->peerdn);
+          ptr++;
           }
 
         rmt_dlv_checked_write(fd, 'X', '1', big_buffer, ptr - big_buffer);
@@ -4376,9 +4499,7 @@ for (delivery_count = 0; addr_remote; delivery_count++)
 # ifndef DISABLE_OCSP
       if (addr->ocsp > OCSP_NOT_REQ)
        {
-       ptr = big_buffer;
-       sprintf(CS ptr, "%c", addr->ocsp + '0');
-       while(*ptr++);
+       ptr = big_buffer + sprintf(CS big_buffer, "%c", addr->ocsp + '0') + 1;
         rmt_dlv_checked_write(fd, 'X', '4', big_buffer, ptr - big_buffer);
        }
 # endif
@@ -4386,23 +4507,17 @@ for (delivery_count = 0; addr_remote; delivery_count++)
 
       if (client_authenticator)
         {
-        ptr = big_buffer;
-       sprintf(CS big_buffer, "%.64s", client_authenticator);
-        while(*ptr++);
+       ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticator) + 1;
         rmt_dlv_checked_write(fd, 'C', '1', big_buffer, ptr - big_buffer);
        }
       if (client_authenticated_id)
         {
-        ptr = big_buffer;
-       sprintf(CS big_buffer, "%.64s", client_authenticated_id);
-        while(*ptr++);
+        ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticated_id) + 1;
         rmt_dlv_checked_write(fd, 'C', '2', big_buffer, ptr - big_buffer);
        }
       if (client_authenticated_sender)
         {
-        ptr = big_buffer;
-       sprintf(CS big_buffer, "%.64s", client_authenticated_sender);
-        while(*ptr++);
+        ptr = big_buffer + sprintf(CS big_buffer, "%.64s", client_authenticated_sender) + 1;
         rmt_dlv_checked_write(fd, 'C', '3', big_buffer, ptr - big_buffer);
        }
 
@@ -4433,19 +4548,34 @@ for (delivery_count = 0; addr_remote; delivery_count++)
         rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer);
         }
 
+#ifdef SUPPORT_SOCKS
+      if (LOGGING(proxy) && proxy_session)
+       {
+       ptr = big_buffer;
+       if (proxy_local_address)
+         {
+         DEBUG(D_deliver) debug_printf("proxy_local_address '%s'\n", proxy_local_address);
+         ptr = big_buffer + sprintf(CS ptr, "%.128s", proxy_local_address) + 1;
+         DEBUG(D_deliver) debug_printf("proxy_local_port %d\n", proxy_local_port);
+         memcpy(ptr, &proxy_local_port, sizeof(proxy_local_port));
+         ptr += sizeof(proxy_local_port);
+         }
+       else
+         *ptr++ = '\0';
+       rmt_dlv_checked_write(fd, 'A', '2', big_buffer, ptr - big_buffer);
+       }
+#endif
+
 #ifdef EXPERIMENTAL_DSN_INFO
 /*um, are they really per-addr?  Other per-conn stuff is not (auth, tls).  But host_used is! */
       if (addr->smtp_greeting)
        {
-       ptr = big_buffer;
        DEBUG(D_deliver) debug_printf("smtp_greeting '%s'\n", addr->smtp_greeting);
-        sprintf(CS ptr, "%.128s", addr->smtp_greeting);
-        while(*ptr++);
+       ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->smtp_greeting) + 1;
        if (addr->helo_response)
          {
          DEBUG(D_deliver) debug_printf("helo_response '%s'\n", addr->helo_response);
-         sprintf(CS ptr, "%.128s", addr->helo_response);
-         while(*ptr++);
+         ptr += sprintf(CS ptr, "%.128s", addr->helo_response) + 1;
          }
        else
          *ptr++ = '\0';
@@ -4455,8 +4585,7 @@ for (delivery_count = 0; addr_remote; delivery_count++)
 
       /* The rest of the information goes in an 'A0' item. */
 
-      sprintf(CS big_buffer, "%c%c", addr->transport_return,
-        addr->special_action);
+      sprintf(CS big_buffer, "%c%c", addr->transport_return, addr->special_action);
       ptr = big_buffer + 2;
       memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno));
       ptr += sizeof(addr->basic_errno);
@@ -4466,23 +4595,15 @@ for (delivery_count = 0; addr_remote; delivery_count++)
       ptr += sizeof(addr->flags);
 
       if (!addr->message) *ptr++ = 0; else
-        {
-        sprintf(CS ptr, "%.1024s", addr->message);
-        while(*ptr++);
-        }
+        ptr += sprintf(CS ptr, "%.1024s", addr->message) + 1;
 
       if (!addr->user_message) *ptr++ = 0; else
-        {
-        sprintf(CS ptr, "%.1024s", addr->user_message);
-        while(*ptr++);
-        }
+        ptr += sprintf(CS ptr, "%.1024s", addr->user_message) + 1;
 
       if (!addr->host_used) *ptr++ = 0; else
         {
-        sprintf(CS ptr, "%.256s", addr->host_used->name);
-        while(*ptr++);
-        sprintf(CS ptr, "%.64s", addr->host_used->address);
-        while(*ptr++);
+        ptr += sprintf(CS ptr, "%.256s", addr->host_used->name) + 1;
+        ptr += sprintf(CS ptr, "%.64s", addr->host_used->address) + 1;
         memcpy(ptr, &(addr->host_used->port), sizeof(addr->host_used->port));
         ptr += sizeof(addr->host_used->port);
 
@@ -4501,12 +4622,9 @@ for (delivery_count = 0; addr_remote; delivery_count++)
     if (LOGGING(incoming_interface) && sending_ip_address)
 #endif
       {
-      uschar * ptr = big_buffer;
-      sprintf(CS ptr, "%.128s", sending_ip_address);
-      while(*ptr++);
-      sprintf(CS ptr, "%d", sending_port);
-      while(*ptr++);
-
+      uschar * ptr;
+      ptr = big_buffer + sprintf(CS big_buffer, "%.128s", sending_ip_address) + 1;
+      ptr += sprintf(CS ptr, "%d", sending_port) + 1;
       rmt_dlv_checked_write(fd, 'I', '0', big_buffer, ptr - big_buffer);
       }
 
@@ -4532,7 +4650,7 @@ for (delivery_count = 0; addr_remote; delivery_count++)
     (void)close(pfd[pipe_read]);
     panicmsg = string_sprintf("fork failed for remote delivery to %s: %s",
         addr->domain, strerror(errno));
-    goto panic_continue;
+    goto enq_continue;
     }
 
   /* Fork succeeded; increment the count, and remember relevant data for
@@ -4567,6 +4685,8 @@ for (delivery_count = 0; addr_remote; delivery_count++)
 
   continue;
 
+enq_continue:
+  if (serialize_key) enq_end(serialize_key);
 panic_continue:
   remote_post_process(addr, LOG_MAIN|LOG_PANIC, panicmsg, fallback);
   continue;
@@ -5649,7 +5769,7 @@ if (process_recipients != RECIP_IGNORE)
       recipient_item *r = recipients_list + i;
       address_item *new = deliver_make_addr(r->address, FALSE);
       new->prop.errors_address = r->errors_to;
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
       if ((new->prop.utf8_msg = message_smtputf8))
        {
        new->prop.utf8_downcvt =       message_utf8_downconvert == 1;
@@ -5664,7 +5784,7 @@ if (process_recipients != RECIP_IGNORE)
       if (r->pno >= 0)
         new->onetime_parent = recipients_list[r->pno].address;
 
-      /* If DSN support is enabled, set the dsn flags and the original receipt 
+      /* If DSN support is enabled, set the dsn flags and the original receipt
          to be passed on to other DSN enabled MTAs */
       new->dsn_flags = r->dsn_flags & rf_dsnflags;
       new->dsn_orcpt = r->orcpt;
@@ -5740,7 +5860,7 @@ if (process_recipients != RECIP_IGNORE)
         break;
         }
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
       if (process_recipients != RECIP_ACCEPT)
        {
        uschar * save_local =  deliver_localpart;
@@ -6588,7 +6708,6 @@ if (addr_local)
 so just queue them all. */
 
 if (queue_run_local)
-  {
   while (addr_remote)
     {
     address_item *addr = addr_remote;
@@ -6598,7 +6717,6 @@ if (queue_run_local)
     addr->message = US"remote deliveries suppressed";
     (void)post_process_one(addr, DEFER, LOG_MAIN, DTYPE_TRANSPORT, 0);
     }
-  }
 
 /* Handle remote deliveries */
 
@@ -6722,16 +6840,13 @@ prevents actual delivery. */
 else if (!dont_deliver)
   retry_update(&addr_defer, &addr_failed, &addr_succeed);
 
-/* Send DSN for successful messages */
-addr_dsntmp = addr_succeed;
+/* Send DSN for successful messages if requested */
 addr_senddsn = NULL;
 
-while(addr_dsntmp)
+for (addr_dsntmp = addr_succeed; addr_dsntmp; addr_dsntmp = addr_dsntmp->next)
   {
   /* af_ignore_error not honored here. it's not an error */
-  DEBUG(D_deliver)
-    {
-    debug_printf("DSN: processing router : %s\n"
+  DEBUG(D_deliver) debug_printf("DSN: processing router : %s\n"
       "DSN: processing successful delivery address: %s\n"
       "DSN: Sender_address: %s\n"
       "DSN: orcpt: %s  flags: %d\n"
@@ -6746,7 +6861,6 @@ while(addr_dsntmp)
       addr_dsntmp->address,
       addr_dsntmp->dsn_aware
       );
-    }
 
   /* send report if next hop not DSN aware or a router flagged "last DSN hop"
      and a report was requested */
@@ -6765,9 +6879,7 @@ while(addr_dsntmp)
     addr_senddsn->next = addr_next;
     }
   else
-    DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n"); 
-
-  addr_dsntmp = addr_dsntmp->next;
+    DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n");
   }
 
 if (addr_senddsn)
@@ -6775,11 +6887,11 @@ if (addr_senddsn)
   pid_t pid;
   int fd;
 
-  /* create exim process to send message */      
+  /* create exim process to send message */
   pid = child_open_exim(&fd);
 
   DEBUG(D_deliver) debug_printf("DSN: child_open_exim returns: %d\n", pid);
-     
+
   if (pid < 0)  /* Creation of child failed */
     {
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to "
@@ -6787,24 +6899,24 @@ if (addr_senddsn)
       getppid(), strerror(errno));
 
     DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n");
-    }    
+    }
   else  /* Creation of child succeeded */
     {
     FILE *f = fdopen(fd, "wb");
     /* header only as required by RFC. only failure DSN needs to honor RET=FULL */
     int topt = topt_add_return_path | topt_no_body;
     uschar * bound;
-     
+
     DEBUG(D_deliver)
       debug_printf("sending error message to: %s\n", sender_address);
-  
+
     /* build unique id for MIME boundary */
     bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
     DEBUG(D_deliver) debug_printf("DSN: MIME boundary: %s\n", bound);
-  
+
     if (errors_reply_to)
       fprintf(f, "Reply-To: %s\n", errors_reply_to);
+
     fprintf(f, "Auto-Submitted: auto-generated\n"
        "From: Mail Delivery System <Mailer-Daemon@%s>\n"
        "To: %s\n"
@@ -6814,7 +6926,7 @@ if (addr_senddsn)
 
        "--%s\n"
        "Content-type: text/plain; charset=us-ascii\n\n"
-   
+
        "This message was created automatically by mail delivery software.\n"
        " ----- The following addresses had successful delivery notifications -----\n",
       qualify_domain_sender, sender_address, bound, bound);
@@ -6841,7 +6953,7 @@ if (addr_senddsn)
       if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
         fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid);
       else
-        fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n");
+        fprintf(f, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n");
       }
     fputc('\n', f);
 
@@ -6866,11 +6978,11 @@ if (addr_senddsn)
       }
 
     fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound);
-           
+
     fflush(f);
     transport_filter_argv = NULL;   /* Just in case */
     return_path = sender_address;   /* In case not previously set */
-           
+
     /* Write the original email out */
     transport_write_message(NULL, fileno(f), topt, 0, NULL, NULL, NULL, NULL, NULL, 0);
     fflush(f);
@@ -7207,7 +7319,7 @@ wording. */
         }
 
       /* output machine readable part */
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
       if (message_smtputf8)
        fprintf(f, "--%s\n"
            "Content-type: message/global-delivery-status\n\n"
@@ -7227,10 +7339,10 @@ wording. */
         if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
           fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid);
         else
-          fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n");
+          fprintf(f, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n");
         }
       fputc('\n', f);
+
       for (addr = handled_addr; addr; addr = addr->next)
         {
        host_item * hu;
@@ -7241,8 +7353,7 @@ wording. */
         if ((hu = addr->host_used) && hu->name)
          {
          const uschar * s;
-         fprintf(f, "Remote-MTA: dns; %s\n",
-           hu->name);
+         fprintf(f, "Remote-MTA: dns; %s\n", hu->name);
 #ifdef EXPERIMENTAL_DSN_INFO
          if (hu->address)
            {
@@ -7270,16 +7381,16 @@ wording. */
       emf_text = next_emf(emf, US"copy");
 
       /* add message body
-         we ignore the intro text from template and add 
+         we ignore the intro text from template and add
          the text for bounce_return_size_limit at the end.
-  
+
          bounce_return_message is ignored
          in case RET= is defined we honor these values
          otherwise bounce_return_body is honored.
-         
+
          bounce_return_size_limit is always honored.
       */
-  
+
       fprintf(f, "--%s\n", bound);
 
       dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned";
@@ -7290,6 +7401,9 @@ wording. */
       if (dsn_ret == dsn_ret_hdrs)
         topt |= topt_no_body;
       else
+       {
+       struct stat statbuf;
+
         /* no full body return at all? */
         if (!bounce_return_body)
           {
@@ -7298,18 +7412,20 @@ wording. */
           if (dsn_ret == dsn_ret_full)
             dsnnotifyhdr = dsnlimitmsg;
           }
+       /* line length limited... return headers only if oversize */
         /* size limited ... return headers only if limit reached */
-        else if (bounce_return_size_limit > 0)
-          {
-          struct stat statbuf;
-          if (fstat(deliver_datafile, &statbuf) == 0 && statbuf.st_size > max)
-            {
-              topt |= topt_no_body;
-              dsnnotifyhdr = dsnlimitmsg;
-            }
+       else if (  max_received_linelength > bounce_return_linesize_limit
+               || (  bounce_return_size_limit > 0
+                  && fstat(deliver_datafile, &statbuf) == 0
+                  && statbuf.st_size > max
+               )  )
+         {
+         topt |= topt_no_body;
+         dsnnotifyhdr = dsnlimitmsg;
           }
-  
-#ifdef EXPERIMENTAL_INTERNATIONAL
+       }
+
+#ifdef SUPPORT_I18N
       if (message_smtputf8)
        fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n"
                                  : "Content-type: message/global\n\n",
@@ -7326,11 +7442,11 @@ wording. */
       transport_write_message(NULL, fileno(f), topt,
         0, dsnnotifyhdr, NULL, NULL, NULL, NULL, 0);
       fflush(f);
+
       /* we never add the final text. close the file */
       if (emf)
         (void)fclose(emf);
+
       fprintf(f, "\n--%s--\n", bound);
 
       /* Close the file, which should send an EOF to the child process
@@ -7445,10 +7561,10 @@ if (!addr_defer)
   /* Unset deliver_freeze so that we won't try to move the spool files further down */
   deliver_freeze = FALSE;
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
   (void) event_raise(event_action, US"msg:complete", NULL);
 #endif
-}
+  }
 
 /* If there are deferred addresses, we are keeping this message because it is
 not yet completed. Lose any temporary files that were catching output from
@@ -7637,6 +7753,7 @@ else if (addr_defer != (address_item *)(+1))
         FILE *wmf = NULL;
         FILE *f = fdopen(fd, "wb");
        uschar * bound;
+        int topt;
 
         if (warn_message_file)
           if (!(wmf = Ufopen(warn_message_file, "rb")))
@@ -7746,7 +7863,7 @@ else if (addr_defer != (address_item *)(+1))
            "Reporting-MTA: dns; %s\n",
          bound,
          smtp_active_hostname);
+
 
         if (dsn_envid)
          {
@@ -7755,7 +7872,7 @@ else if (addr_defer != (address_item *)(+1))
           if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0)
             fprintf(f,"Original-Envelope-ID: %s\n", dsn_envid);
           else
-            fprintf(f,"X-Original-Envelope-ID: error decoding xtext formated ENVID\n");
+            fprintf(f,"X-Original-Envelope-ID: error decoding xtext formatted ENVID\n");
           }
         fputc('\n', f);
 
@@ -7770,7 +7887,7 @@ else if (addr_defer != (address_item *)(+1))
            addr_dsndefer->address);
           if (addr_dsndefer->host_used && addr_dsndefer->host_used->name)
             {
-            fprintf(f, "Remote-MTA: dns; %s\n", 
+            fprintf(f, "Remote-MTA: dns; %s\n",
                    addr_dsndefer->host_used->name);
             print_dsn_diagnostic_code(addr_dsndefer, f);
             }
@@ -7783,7 +7900,7 @@ else if (addr_defer != (address_item *)(+1))
 
         fflush(f);
         /* header only as required by RFC. only failure DSN needs to honor RET=FULL */
-        int topt = topt_add_return_path | topt_no_body;
+        topt = topt_add_return_path | topt_no_body;
         transport_filter_argv = NULL;   /* Just in case */
         return_path = sender_address;   /* In case not previously set */
         /* Write the original email out */
@@ -7955,7 +8072,7 @@ if (!regex_PRDR) regex_PRDR =
   regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
 #endif
 
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
 if (!regex_UTF8) regex_UTF8 =
   regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE);
 #endif