Events: add recipient-deferred events, both per-host and all-hosts.
[exim.git] / src / src / deliver.c
index 6a3df89bb2170abbfac927b4e8b22c0ee0993a9d..65f148c07b93aadabd62b11d4a37d4303b5f2d33 100644 (file)
@@ -785,12 +785,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,9 +804,13 @@ 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;
@@ -1288,9 +1294,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);
@@ -1929,7 +1943,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;
@@ -2315,17 +2329,47 @@ if (addr->special_action == SPECIAL_WARN && addr->transport->warn_message)
 
 
 
-/* Put the chain of addrs on the defer list.  Retry will happen
-on the next queue run, earlier if triggered by a new message.
-Loop for the next set of addresses. */
+/* 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 void
-deferlist_chain(address_item * addr)
+static BOOL
+tpt_parallel_check(transport_instance * tp, address_item * addr, uschar ** key)
 {
-address_item * next;
-for (next = addr; next->next; next = next->next) ;
-next->next = addr_defer;
-addr_defer = addr;
+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;
 }
 
 
@@ -2632,33 +2676,18 @@ while (addr_local)
   We use a hints DB entry, incremented here and decremented after
   the transport (and any shadow transport) completes. */
 
-  if (tp->max_parallel)
+  if (tpt_parallel_check(tp, addr, &serialize_key))
     {
-    int_eximarith_t max_parallel =
-      expand_string_integer(tp->max_parallel, TRUE);
     if (expand_string_message)
       {
       logflags |= LOG_PANIC;
-      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);
-      for (addr2 = addr->next; addr; addr = addr2, addr2 = addr2->next)
+      do
+       {
+       addr = addr->next;
        post_process_one(addr, DEFER, logflags, DTYPE_TRANSPORT, 0);
-      continue;
-      }
-    if (  max_parallel > 0
-       && !enq_start(
-           serialize_key = string_sprintf("tpt-serialize-%s", tp->name),
-           (unsigned) max_parallel)
-       )
-      {
-      DEBUG(D_transport)
-       debug_printf("skipping tpt %s because parallelism limit %u reached\n",
-                   tp->name, (unsigned) max_parallel);
-
-      deferlist_chain(addr);
-      continue;
+       } while ((addr = addr2));
       }
+    continue;                  /* Loop for the next set of addresses. */
     }
 
 
@@ -3045,7 +3074,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 */
@@ -3058,7 +3087,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 ? */
@@ -3071,16 +3100,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;
@@ -3088,7 +3117,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. */
 
@@ -3807,7 +3836,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;
   }
@@ -3823,7 +3852,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)
@@ -4091,29 +4120,11 @@ for (delivery_count = 0; addr_remote; delivery_count++)
   The hints DB entry is decremented in par_reduce(), when we reap the
   transport process. */
 
-  if (tp->max_parallel)
-    {
-    int_eximarith_t max_parallel =
-      expand_string_integer(tp->max_parallel, TRUE);
-    if (expand_string_message)
-      {
-      panicmsg = expand_string_message;
+  if (tpt_parallel_check(tp, addr, &serialize_key))
+    if ((panicmsg = expand_string_message))
       goto panic_continue;
-      }
-    if (  max_parallel > 0
-       && !enq_start(
-           serialize_key = string_sprintf("tpt-serialize-%s", tp->name),
-           (unsigned) max_parallel)
-       )
-      {
-      DEBUG(D_transport)
-       debug_printf("skipping tpt %s because parallelism limit %u reached\n",
-                   tp->name, (unsigned) max_parallel);
-
-      deferlist_chain(addr);
-      continue;
-      }
-    }
+    else
+      continue;                        /* Loop for the next set of addresses. */
 
   /* Set up the expansion variables for this set of addresses */
 
@@ -4207,7 +4218,11 @@ for (delivery_count = 0; addr_remote; delivery_count++)
         }
 
       else
-       deferlist_chain(addr);
+       {
+       while (next->next) next = next->next;
+       next->next = addr_defer;
+       addr_defer = addr;
+       }
 
       continue;
       }
@@ -5750,7 +5765,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;
@@ -6674,7 +6689,6 @@ if (addr_local)
 so just queue them all. */
 
 if (queue_run_local)
-  {
   while (addr_remote)
     {
     address_item *addr = addr_remote;
@@ -6684,7 +6698,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 */
 
@@ -6851,7 +6864,7 @@ while(addr_dsntmp)
     addr_senddsn->next = addr_next;
     }
   else
-    DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n"); 
+    DEBUG(D_deliver) debug_printf("DSN: not sending DSN success message\n");
 
   addr_dsntmp = addr_dsntmp->next;
   }
@@ -6861,11 +6874,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 "
@@ -6873,24 +6886,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"
@@ -6900,7 +6913,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);
@@ -6952,11 +6965,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);
@@ -7316,7 +7329,7 @@ wording. */
           fprintf(f, "X-Original-Envelope-ID: error decoding xtext formated ENVID\n");
         }
       fputc('\n', f);
+
       for (addr = handled_addr; addr; addr = addr->next)
         {
        host_item * hu;
@@ -7327,8 +7340,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)
            {
@@ -7356,16 +7368,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";
@@ -7394,7 +7406,7 @@ wording. */
               dsnnotifyhdr = dsnlimitmsg;
             }
           }
-  
+
 #ifdef EXPERIMENTAL_INTERNATIONAL
       if (message_smtputf8)
        fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n"
@@ -7412,11 +7424,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
@@ -7832,7 +7844,7 @@ else if (addr_defer != (address_item *)(+1))
            "Reporting-MTA: dns; %s\n",
          bound,
          smtp_active_hostname);
+
 
         if (dsn_envid)
          {
@@ -7856,7 +7868,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);
             }