Michael Haardt's patch for support for :user and :subaddress in Sieve
[exim.git] / src / src / deliver.c
index 741d7b79cc31baec27ec1ba1a7d9aa64e9c8df50..ac23a33aa3a41a02410bde87008ac118e9efb8dd 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/deliver.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
+/* $Cambridge: exim/src/src/deliver.c,v 1.11 2005/04/06 14:40:24 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2005 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* The main code for delivering a message. */
@@ -156,6 +156,13 @@ deliver_localpart_data = addr->p.localpart_data;
 deliver_domain = addr->domain;
 self_hostname = addr->self_hostname;
 
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+bmi_deliver = 1;    /* deliver by default */
+bmi_alt_location = NULL;
+bmi_base64_verdict = NULL;
+bmi_base64_tracker_verdict = NULL;
+#endif
+
 /* If there's only one address we can set everything. */
 
 if (addr->next == NULL)
@@ -205,6 +212,19 @@ if (addr->next == NULL)
       deliver_localpart_suffix = addr->parent->suffix;
       }
     }
+
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+    /* Set expansion variables related to Brightmail AntiSpam */
+    bmi_base64_verdict = bmi_get_base64_verdict(deliver_localpart_orig, deliver_domain_orig);
+    bmi_base64_tracker_verdict = bmi_get_base64_tracker_verdict(bmi_base64_verdict);
+    /* get message delivery status (0 - don't deliver | 1 - deliver) */
+    bmi_deliver = bmi_get_delivery_status(bmi_base64_verdict);
+    /* if message is to be delivered, get eventual alternate location */
+    if (bmi_deliver == 1) {
+      bmi_alt_location = bmi_get_alt_location(bmi_base64_verdict);
+    };
+#endif
+
   }
 
 /* For multiple addresses, don't set local part, and leave the domain and
@@ -709,9 +729,27 @@ else if (driver_type == DTYPE_ROUTER)
 
 /* If there's an error message set, ensure that it contains only printing
 characters - it should, but occasionally things slip in and this at least
-stops the log format from getting wrecked. */
+stops the log format from getting wrecked. We also scan the message for an LDAP
+expansion item that has a password setting, and flatten the password. This is a
+fudge, but I don't know a cleaner way of doing this. (If the item is badly
+malformed, it won't ever have gone near LDAP.) */
 
-if (addr->message != NULL) addr->message = string_printing(addr->message);
+if (addr->message != NULL)
+  {
+  addr->message = string_printing(addr->message);
+  if (Ustrstr(addr->message, "failed to expand") != NULL &&
+      (Ustrstr(addr->message, "ldap:") != NULL ||
+       Ustrstr(addr->message, "ldapdn:") != NULL ||
+       Ustrstr(addr->message, "ldapm:") != NULL))
+    {
+    uschar *p = Ustrstr(addr->message, "pass=");
+    if (p != NULL)
+      {
+      p += 5;
+      while (*p != 0 && !isspace(*p)) *p++ = 'x';
+      }
+    }
+  }
 
 /* If we used a transport that has one of the "return_output" options set, and
 if it did in fact generate some output, then for return_output we treat the
@@ -1046,12 +1084,15 @@ else
     setflag(addr, af_ignore_error);
 
   /* Freeze the message if requested, or if this is a bounce message (or other
-  message with null sender). However, don't freeze if errors are being ignored.
-  The actual code to ignore occurs later, instead of sending a message. Logging
-  of freezing occurs later, just before writing the -H file. */
+  message with null sender) and this address does not have its own errors
+  address. However, don't freeze if errors are being ignored. The actual code
+  to ignore occurs later, instead of sending a message. Logging of freezing
+  occurs later, just before writing the -H file. */
 
   if (!testflag(addr, af_ignore_error) &&
-      (addr->special_action == SPECIAL_FREEZE || sender_address[0] == 0))
+      (addr->special_action == SPECIAL_FREEZE ||
+        (sender_address[0] == 0 && addr->p.errors_address == NULL)
+      ))
     {
     frozen_info = (addr->special_action == SPECIAL_FREEZE)? US"" :
       (sender_local && !local_error_message)?
@@ -1408,18 +1449,21 @@ return rc;
 *************************************************/
 
 /* Check that this base address hasn't previously been delivered to its routed
-transport. The check is necessary at delivery time in order to handle homonymic
-addresses correctly in cases where the pattern of redirection changes between
-delivery attempts (so the unique fields change). Non-homonymic previous
-delivery is detected earlier, at routing time (which saves unnecessary
-routing).
+transport. If it has been delivered, mark it done. The check is necessary at
+delivery time in order to handle homonymic addresses correctly in cases where
+the pattern of redirection changes between delivery attempts (so the unique
+fields change). Non-homonymic previous delivery is detected earlier, at routing
+time (which saves unnecessary routing).
+
+Arguments:
+  addr      the address item
+  testing   TRUE if testing wanted only, without side effects
 
-Argument:   the address item
 Returns:    TRUE if previously delivered by the transport
 */
 
 static BOOL
-previously_transported(address_item *addr)
+previously_transported(address_item *addr, BOOL testing)
 {
 (void)string_format(big_buffer, big_buffer_size, "%s/%s",
   addr->unique + (testflag(addr, af_homonym)? 3:0), addr->transport->name);
@@ -1429,7 +1473,7 @@ if (tree_search(tree_nonrecipients, big_buffer) != 0)
   DEBUG(D_deliver|D_route|D_transport)
     debug_printf("%s was previously delivered (%s transport): discarded\n",
     addr->address, addr->transport->name);
-  child_done(addr, tod_stamp(tod_log));
+  if (!testing) child_done(addr, tod_stamp(tod_log));
   return TRUE;
   }
 
@@ -2016,7 +2060,7 @@ while (addr_local != NULL)
   attempts. Non-homonymic previous delivery is detected earlier, at routing
   time. */
 
-  if (previously_transported(addr)) continue;
+  if (previously_transported(addr, FALSE)) continue;
 
   /* There are weird cases where logging is disabled */
 
@@ -2057,6 +2101,7 @@ while (addr_local != NULL)
     same characteristics. These are:
 
       same transport
+      not previously delivered (see comment about 50 lines above)
       same local part if the transport's configuration contains $local_part
       same domain if the transport's configuration contains $domain
       same errors address
@@ -2070,6 +2115,7 @@ while (addr_local != NULL)
       {
       BOOL ok =
         tp == next->transport &&
+        !previously_transported(next, TRUE) &&
         (!uses_lp  || Ustrcmp(next->local_part, addr->local_part) == 0) &&
         (!uses_dom || Ustrcmp(next->domain, addr->domain) == 0) &&
         same_strings(next->p.errors_address, addr->p.errors_address) &&
@@ -2533,13 +2579,13 @@ also by optional retry data.
 
 Read in large chunks into the big buffer and then scan through, interpreting
 the data therein. In most cases, only a single read will be necessary. No
-individual item will ever be anywhere near 500 bytes in length, so by ensuring
-that we read the next chunk when there is less than 500 bytes left in the
-non-final chunk, we can assume each item is complete in store before handling
-it. Actually, each item is written using a single write(), which is atomic for
-small items (less than PIPE_BUF, which seems to be at least 512 in any Unix) so
-even if we are reading while the subprocess is still going, we should never
-have only a partial item in the buffer.
+individual item will ever be anywhere near 2500 bytes in length, so by ensuring
+that we read the next chunk when there is less than 2500 bytes left in the
+non-final chunk, we can assume each item is complete in the buffer before
+handling it. Each item is written using a single write(), which is atomic for
+small items (less than PIPE_BUF, which seems to be at least 512 in any Unix and
+often bigger) so even if we are reading while the subprocess is still going, we
+should never have only a partial item in the buffer.
 
 Argument:
   poffset     the offset of the parlist item
@@ -2575,7 +2621,9 @@ completed.
 
 Each separate item is written to the pipe in a single write(), and as they are
 all short items, the writes will all be atomic and we should never find
-ourselves in the position of having read an incomplete item. */
+ourselves in the position of having read an incomplete item. "Short" in this
+case can mean up to about 1K in the case when there is a long error message
+associated with an address. */
 
 DEBUG(D_deliver) debug_printf("reading pipe for subprocess %d (%s)\n",
   (int)p->pid, eop? "ended" : "not ended");
@@ -2589,7 +2637,7 @@ while (!done)
   There will be only one read if we get all the available data (i.e. don't
   fill the buffer completely). */
 
-  if (remaining < 500 && unfinished)
+  if (remaining < 2500 && unfinished)
     {
     int len;
     int available = big_buffer_size - remaining;
@@ -3345,7 +3393,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
   attempts. Non-homonymic previous delivery is detected earlier, at routing
   time. */
 
-  if (previously_transported(addr)) continue;
+  if (previously_transported(addr, FALSE)) continue;
 
   /* Force failure if the message is too big. */
 
@@ -4614,6 +4662,8 @@ else if (system_filter != NULL && process_recipients != RECIP_FAIL_TIMEOUT)
       RDO_REWRITE,
     NULL,                   /* No :include: restriction (not used in filter) */
     NULL,                   /* No sieve vacation directory (not sieve!) */
+    NULL,                   /* No sieve user address (not sieve!) */
+    NULL,                   /* No sieve subaddress (not sieve!) */
     &ugid,                  /* uid/gid data */
     &addr_new,              /* Where to hang generated addresses */
     &filter_message,        /* Where to put error message */
@@ -6319,7 +6369,14 @@ if (addr_defer == NULL)
   sprintf(CS spoolname, "%s/input/%s/%s-H", spool_directory, message_subdir, id);
   if (Uunlink(spoolname) < 0)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to unlink %s", spoolname);
-  log_write(0, LOG_MAIN, "Completed");
+
+  /* Log the end of this message, with queue time if requested. */
+
+  if ((log_extra_selector & LX_queue_time_overall) != 0)
+    log_write(0, LOG_MAIN, "Completed QT=%s",
+      readconf_printtime(time(NULL) - received_time));
+  else
+    log_write(0, LOG_MAIN, "Completed");
   }
 
 /* If there are deferred addresses, we are keeping this message because it is