defer_ok was not working if used on a callout with verify=header_sender.
[exim.git] / src / src / acl.c
index 1f767b65ddae543526e048f126a23e2699b7f40f..47c4f86a258f906f296546e2782cfc40e7b03d5c 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/acl.c,v 1.2 2004/10/18 11:36:23 ph10 Exp $ */
+/* $Cambridge: exim/src/src/acl.c,v 1.18 2005/01/27 10:26:14 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. */
 
 /* Code for handling Access Control Lists (ACLs) */
@@ -34,19 +34,77 @@ static int msgcond[] = { FAIL, OK, OK, FAIL, OK, FAIL, OK };
 /* ACL condition and modifier codes - keep in step with the table that
 follows. */
 
-enum { ACLC_ACL, ACLC_AUTHENTICATED, ACLC_CONDITION, ACLC_CONTROL, ACLC_DELAY,
-  ACLC_DNSLISTS, ACLC_DOMAINS, ACLC_ENCRYPTED, ACLC_ENDPASS, ACLC_HOSTS,
-  ACLC_LOCAL_PARTS, ACLC_LOG_MESSAGE, ACLC_LOGWRITE, ACLC_MESSAGE,
-  ACLC_RECIPIENTS, ACLC_SENDER_DOMAINS, ACLC_SENDERS, ACLC_SET, ACLC_VERIFY };
+enum { ACLC_ACL, ACLC_AUTHENTICATED,
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+       ACLC_BMI_OPTIN,
+#endif
+ACLC_CONDITION, ACLC_CONTROL,
+#ifdef WITH_CONTENT_SCAN
+       ACLC_DECODE,
+#endif
+       ACLC_DELAY,
+#ifdef WITH_OLD_DEMIME
+       ACLC_DEMIME,
+#endif        
+       ACLC_DNSLISTS, ACLC_DOMAINS, ACLC_ENCRYPTED, ACLC_ENDPASS,
+       ACLC_HOSTS, ACLC_LOCAL_PARTS, ACLC_LOG_MESSAGE, ACLC_LOGWRITE,
+#ifdef WITH_CONTENT_SCAN
+       ACLC_MALWARE,
+#endif
+       ACLC_MESSAGE,
+#ifdef WITH_CONTENT_SCAN
+       ACLC_MIME_REGEX,
+#endif
+       ACLC_RECIPIENTS,
+#ifdef WITH_CONTENT_SCAN
+       ACLC_REGEX,
+#endif
+       ACLC_SENDER_DOMAINS, ACLC_SENDERS, ACLC_SET,
+#ifdef WITH_CONTENT_SCAN
+       ACLC_SPAM,       
+#endif
+#ifdef EXPERIMENTAL_SPF
+       ACLC_SPF,
+#endif
+       ACLC_VERIFY };
 
 /* ACL conditions/modifiers: "delay", "control", "endpass", "message",
 "log_message", "logwrite", and "set" are modifiers that look like conditions
 but always return TRUE. They are used for their side effects. */
 
-static uschar *conditions[] = { US"acl", US"authenticated", US"condition",
-  US"control", US"delay", US"dnslists", US"domains", US"encrypted",
+static uschar *conditions[] = { US"acl", US"authenticated",
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+  US"bmi_optin",
+#endif
+  US"condition",
+  US"control", 
+#ifdef WITH_CONTENT_SCAN
+  US"decode",
+#endif
+  US"delay",
+#ifdef WITH_OLD_DEMIME
+  US"demime",
+#endif
+  US"dnslists", US"domains", US"encrypted",
   US"endpass", US"hosts", US"local_parts", US"log_message", US"logwrite",
-  US"message", US"recipients", US"sender_domains", US"senders", US"set",
+#ifdef WITH_CONTENT_SCAN
+  US"malware",
+#endif
+  US"message",
+#ifdef WITH_CONTENT_SCAN
+  US"mime_regex",
+#endif
+  US"recipients",
+#ifdef WITH_CONTENT_SCAN
+  US"regex",
+#endif
+  US"sender_domains", US"senders", US"set",
+#ifdef WITH_CONTENT_SCAN
+  US"spam",
+#endif
+#ifdef EXPERIMENTAL_SPF
+  US"spf",
+#endif
   US"verify" };
   
 /* ACL control names */
@@ -62,9 +120,18 @@ checking functions. */
 static uschar cond_expand_at_top[] = {
   TRUE,    /* acl */
   FALSE,   /* authenticated */
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+  TRUE,    /* bmi_optin */
+#endif  
   TRUE,    /* condition */
   TRUE,    /* control */
+#ifdef WITH_CONTENT_SCAN
+  TRUE,    /* decode */
+#endif
   TRUE,    /* delay */
+#ifdef WITH_OLD_DEMIME
+  TRUE,    /* demime */
+#endif
   TRUE,    /* dnslists */
   FALSE,   /* domains */
   FALSE,   /* encrypted */
@@ -73,11 +140,26 @@ static uschar cond_expand_at_top[] = {
   FALSE,   /* local_parts */
   TRUE,    /* log_message */
   TRUE,    /* logwrite */
+#ifdef WITH_CONTENT_SCAN
+  TRUE,    /* malware */
+#endif
   TRUE,    /* message */
+#ifdef WITH_CONTENT_SCAN
+  TRUE,    /* mime_regex */
+#endif
   FALSE,   /* recipients */
+#ifdef WITH_CONTENT_SCAN
+  TRUE,    /* regex */
+#endif
   FALSE,   /* sender_domains */
   FALSE,   /* senders */
   TRUE,    /* set */
+#ifdef WITH_CONTENT_SCAN
+  TRUE,    /* spam */
+#endif
+#ifdef EXPERIMENTAL_SPF
+  TRUE,    /* spf */
+#endif
   TRUE     /* verify */
 };
 
@@ -86,9 +168,18 @@ static uschar cond_expand_at_top[] = {
 static uschar cond_modifiers[] = {
   FALSE,   /* acl */
   FALSE,   /* authenticated */
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+  TRUE,    /* bmi_optin */
+#endif  
   FALSE,   /* condition */
   TRUE,    /* control */
+#ifdef WITH_CONTENT_SCAN
+  FALSE,   /* decode */
+#endif
   TRUE,    /* delay */
+#ifdef WITH_OLD_DEMIME
+  FALSE,   /* demime */
+#endif
   FALSE,   /* dnslists */
   FALSE,   /* domains */
   FALSE,   /* encrypted */
@@ -96,12 +187,27 @@ static uschar cond_modifiers[] = {
   FALSE,   /* hosts */
   FALSE,   /* local_parts */
   TRUE,    /* log_message */
-  TRUE,    /* log_write */
+  TRUE,    /* logwrite */
+#ifdef WITH_CONTENT_SCAN
+  FALSE,   /* malware */
+#endif
   TRUE,    /* message */
+#ifdef WITH_CONTENT_SCAN
+  FALSE,   /* mime_regex */
+#endif
   FALSE,   /* recipients */
+#ifdef WITH_CONTENT_SCAN
+  FALSE,   /* regex */
+#endif
   FALSE,   /* sender_domains */
   FALSE,   /* senders */
   TRUE,    /* set */
+#ifdef WITH_CONTENT_SCAN
+  FALSE,   /* spam */
+#endif
+#ifdef EXPERIMENTAL_SPF
+  FALSE,   /* spf */
+#endif
   FALSE    /* verify */
 };
 
@@ -110,15 +216,49 @@ each condition, there's a bitmap of dis-allowed times. */
 
 static unsigned int cond_forbids[] = {
   0,                                               /* acl */
+   
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_CONNECT)|   /* authenticated */
     (1<<ACL_WHERE_HELO),
+  
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+  (1<<ACL_WHERE_AUTH)|                             /* bmi_optin */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_MIME)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|                                       
+    (1<<ACL_WHERE_MAILAUTH)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_PREDATA),
+#endif
+  
   0,                                               /* condition */
-
+  
   /* Certain types of control are always allowed, so we let it through
   always and check in the control processing itself */
-
+  
   0,                                               /* control */
+  
+#ifdef WITH_CONTENT_SCAN
+  (1<<ACL_WHERE_AUTH)|                             /* decode */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_PREDATA)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_RCPT),
+#endif
+
   0,                                               /* delay */
+  
+#ifdef WITH_OLD_DEMIME
+  (1<<ACL_WHERE_AUTH)|                             /* demime */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_MIME),
+#endif
+  
   (1<<ACL_WHERE_NOTSMTP),                          /* dnslists */
 
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)|      /* domains */
@@ -131,7 +271,9 @@ static unsigned int cond_forbids[] = {
 
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_CONNECT)|   /* encrypted */
     (1<<ACL_WHERE_HELO),
+     
   0,                                               /* endpass */
+   
   (1<<ACL_WHERE_NOTSMTP),                          /* hosts */
 
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)|      /* local_parts */
@@ -143,9 +285,31 @@ static unsigned int cond_forbids[] = {
     (1<<ACL_WHERE_VRFY),
 
   0,                                               /* log_message */
+   
   0,                                               /* logwrite */
+  
+#ifdef WITH_CONTENT_SCAN
+  (1<<ACL_WHERE_AUTH)|                             /* malware */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_MIME),
+#endif
+
   0,                                               /* message */
 
+#ifdef WITH_CONTENT_SCAN
+  (1<<ACL_WHERE_AUTH)|                             /* mime_regex */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_PREDATA)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_RCPT),
+#endif
+
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)|      /* recipients */
     (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
     (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_PREDATA)|
@@ -154,6 +318,16 @@ static unsigned int cond_forbids[] = {
     (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
     (1<<ACL_WHERE_VRFY),
 
+#ifdef WITH_CONTENT_SCAN
+  (1<<ACL_WHERE_AUTH)|                             /* regex */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_MIME),
+#endif
+
   (1<<ACL_WHERE_AUTH)|(1<<ACL_WHERE_CONNECT)|      /* sender_domains */
     (1<<ACL_WHERE_HELO)|
     (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
@@ -168,6 +342,24 @@ static unsigned int cond_forbids[] = {
 
   0,                                               /* set */
 
+#ifdef WITH_CONTENT_SCAN
+  (1<<ACL_WHERE_AUTH)|                             /* spam */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_RCPT)|(1<<ACL_WHERE_PREDATA)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_MIME),
+#endif
+
+#ifdef EXPERIMENTAL_SPF
+  (1<<ACL_WHERE_AUTH)|(1<<ACL_WHERE_CONNECT)|      /* spf */
+    (1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_MAILAUTH)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_STARTTLS)|(1<<ACL_WHERE_VRFY),
+#endif
+
   /* Certain types of verify are always allowed, so we let it through
   always and check in the verify function itself */
 
@@ -177,32 +369,63 @@ static unsigned int cond_forbids[] = {
 
 /* Return values from decode_control() */
 
-enum { CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, CONTROL_CASELOWER_LOCAL_PART,
+enum { 
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+  CONTROL_BMI_RUN,
+#endif  
+  CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, CONTROL_CASELOWER_LOCAL_PART,
   CONTROL_ENFORCE_SYNC, CONTROL_NO_ENFORCE_SYNC, CONTROL_FREEZE,
-  CONTROL_QUEUE_ONLY, CONTROL_SUBMISSION, CONTROL_NO_MULTILINE };
+  CONTROL_QUEUE_ONLY, CONTROL_SUBMISSION,
+#ifdef WITH_CONTENT_SCAN
+  CONTROL_NO_MBOX_UNSPOOL, 
+#endif
+  CONTROL_FAKEREJECT, CONTROL_NO_MULTILINE };
 
 /* Bit map vector of which controls are not allowed at certain times. For
 each control, there's a bitmap of dis-allowed times. For some, it is easier to
 specify the negation of a small number of allowed times. */
 
 static unsigned int control_forbids[] = {
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+  0,                                               /* bmi_run */
+#endif
+
   0,                                               /* error */
+  
+  (unsigned int) 
   ~(1<<ACL_WHERE_RCPT),                            /* caseful_local_part */
+   
+  (unsigned int) 
   ~(1<<ACL_WHERE_RCPT),                            /* caselower_local_part */
+   
   (1<<ACL_WHERE_NOTSMTP),                          /* enforce_sync */
+   
   (1<<ACL_WHERE_NOTSMTP),                          /* no_enforce_sync */
    
+  (unsigned int) 
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* freeze */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
     (1<<ACL_WHERE_NOTSMTP)),
      
+  (unsigned int) 
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* queue_only */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
     (1<<ACL_WHERE_NOTSMTP)),
      
+  (unsigned int) 
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* submission */
     (1<<ACL_WHERE_PREDATA)),                       
-     
+
+#ifdef WITH_CONTENT_SCAN
+  (unsigned int) 
+  ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* no_mbox_unspool */
+    (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)),
+#endif
+
+  (unsigned int) 
+  ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* fakereject */
+    (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)),
+
   (1<<ACL_WHERE_NOTSMTP)                           /* no_multiline */
 };
 
@@ -215,6 +438,9 @@ typedef struct control_def {
 } control_def;
 
 static control_def controls_list[] = {
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+  { US"bmi_run",                CONTROL_BMI_RUN, FALSE},
+#endif
   { US"caseful_local_part",     CONTROL_CASEFUL_LOCAL_PART, FALSE},
   { US"caselower_local_part",   CONTROL_CASELOWER_LOCAL_PART, FALSE},
   { US"enforce_sync",           CONTROL_ENFORCE_SYNC, FALSE},
@@ -222,6 +448,10 @@ static control_def controls_list[] = {
   { US"no_enforce_sync",        CONTROL_NO_ENFORCE_SYNC, FALSE},
   { US"no_multiline_responses", CONTROL_NO_MULTILINE, FALSE},
   { US"queue_only",             CONTROL_QUEUE_ONLY, FALSE},
+#ifdef WITH_CONTENT_SCAN
+  { US"no_mbox_unspool",        CONTROL_NO_MBOX_UNSPOOL, FALSE},
+#endif
+  { US"fakereject",             CONTROL_FAKEREJECT, TRUE},
   { US"submission",             CONTROL_SUBMISSION, TRUE}
   };
 
@@ -547,6 +777,11 @@ if (hlen > 0)
         newtype = htype_add_rec;
         p += 16;
         }
+      else if (strncmpic(p, US":at_start_rfc:", 14) == 0)
+        {
+        newtype = htype_add_rfc;
+        p += 14;
+        }        
       else if (strncmpic(p, US":at_start:", 10) == 0)
         {
         newtype = htype_add_top;
@@ -689,6 +924,7 @@ acl_verify(int where, address_item *addr, uschar *arg,
 int sep = '/';
 int callout = -1;
 int callout_overall = -1;
+int callout_connect = -1;
 int verify_options = 0;
 int rc;
 BOOL verify_header_sender = FALSE;
@@ -835,6 +1071,11 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
         uschar *opt;
         uschar buffer[256];
         while (isspace(*ss)) ss++;
+        
+        /* This callout option handling code has become a mess as new options 
+        have been added in an ad hoc manner. It should be tidied up into some 
+        kind of table-driven thing. */
         while ((opt = string_nextinlist(&ss, &optsep, buffer, sizeof(buffer)))
               != NULL)
           {
@@ -903,6 +1144,25 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
               return ERROR;
               }
             }
+          else if (strncmpic(opt, US"connect", 7) == 0)
+            {
+            opt += 7;
+            while (isspace(*opt)) opt++;
+            if (*opt++ != '=')
+              {
+              *log_msgptr = string_sprintf("'=' expected after "
+                "\"callout_overaall\" in ACL condition \"%s\"", arg);
+              return ERROR;
+              }
+            while (isspace(*opt)) opt++;
+            callout_connect = readconf_readtime(opt, 0, FALSE);
+            if (callout_connect < 0)
+              {
+              *log_msgptr = string_sprintf("bad time value in ACL condition "
+                "\"verify %s\"", arg);
+              return ERROR;
+              }
+            }
           else    /* Plain time is callout connect/command timeout */
             {
             callout = readconf_readtime(opt, 0, FALSE);
@@ -947,14 +1207,20 @@ message if giving out verification details. */
 
 if (verify_header_sender)
   {
+  int verrno; 
   rc = verify_check_header_address(user_msgptr, log_msgptr, callout,
-    callout_overall, se_mailfrom, pm_mailfrom, verify_options);
-  if (smtp_return_error_details)
-    {
-    if (*user_msgptr == NULL && *log_msgptr != NULL)
-      *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
-    if (rc == DEFER) acl_temp_details = TRUE;
-    }
+    callout_overall, callout_connect, se_mailfrom, pm_mailfrom, verify_options,
+    &verrno);
+  if (rc != OK)
+    { 
+    *basic_errno = verrno;
+    if (smtp_return_error_details)
+      {
+      if (*user_msgptr == NULL && *log_msgptr != NULL)
+        *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
+      if (rc == DEFER) acl_temp_details = TRUE;
+      }
+    }   
   }
 
 /* Handle a sender address. The default is to verify *the* sender address, but
@@ -1012,6 +1278,8 @@ else if (verify_sender_address != NULL)
   else
     {
     BOOL routed = TRUE;
+    uschar *save_address_data = deliver_address_data;
+      
     sender_vaddr = deliver_make_addr(verify_sender_address, TRUE);
     if (no_details) setflag(sender_vaddr, af_sverify_told);
     if (verify_sender_address[0] != 0)
@@ -1029,7 +1297,7 @@ else if (verify_sender_address != NULL)
       verify_options. */
 
       rc = verify_address(sender_vaddr, NULL, verify_options, callout,
-        callout_overall, se_mailfrom, pm_mailfrom, &routed);
+        callout_overall, callout_connect, se_mailfrom, pm_mailfrom, &routed);
 
       HDEBUG(D_acl) debug_printf("----------- end verify ------------\n");
 
@@ -1057,7 +1325,16 @@ else if (verify_sender_address != NULL)
     sender_vaddr->special_action = rc;
     sender_vaddr->next = sender_verified_list;
     sender_verified_list = sender_vaddr;
+    
+    /* Restore the recipient address data, which might have been clobbered by 
+    the sender verification. */
+  
+    deliver_address_data = save_address_data;
     }
+    
+  /* Put the sender address_data value into $sender_address_data */
+
+  sender_address_data = sender_vaddr->p.address_data; 
   }
 
 /* A recipient address just gets a straightforward verify; again we must handle
@@ -1072,10 +1349,12 @@ else
 
   addr2 = *addr;
   rc = verify_address(&addr2, NULL, verify_options|vopt_is_recipient, callout,
-    callout_overall, se_mailfrom, pm_mailfrom, NULL);
+    callout_overall, callout_connect, se_mailfrom, pm_mailfrom, NULL);
   HDEBUG(D_acl) debug_printf("----------- end verify ------------\n");
+  
   *log_msgptr = addr2.message;
-  *user_msgptr = addr2.user_message;
+  *user_msgptr = (addr2.user_message != NULL)? 
+    addr2.user_message : addr2.message;
   *basic_errno = addr2.basic_errno;
 
   /* Make $address_data visible */
@@ -1217,6 +1496,9 @@ uschar *user_message = NULL;
 uschar *log_message = NULL;
 uschar *p;
 int rc = OK;
+#ifdef WITH_CONTENT_SCAN
+int sep = '/';
+#endif
 
 for (; cb != NULL; cb = cb->next)
   {
@@ -1324,6 +1606,17 @@ for (; cb != NULL; cb = cb->next)
         TRUE, NULL);
     break;
 
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+    case ACLC_BMI_OPTIN:
+      {
+      int old_pool = store_pool;
+      store_pool = POOL_PERM;
+      bmi_current_optin = string_copy(arg);
+      store_pool = old_pool;
+      }
+    break;
+#endif
+
     case ACLC_CONDITION:
     if (Ustrspn(arg, "0123456789") == Ustrlen(arg))     /* Digits, or empty */
       rc = (Uatoi(arg) == 0)? FAIL : OK;
@@ -1339,7 +1632,7 @@ for (; cb != NULL; cb = cb->next)
     case ACLC_CONTROL:
     control_type = decode_control(arg, &p, where, log_msgptr);
 
-    /* Check this control makes sense at this time */
+    /* Check if this control makes sense at this time */
 
     if ((control_forbids[control_type] & (1 << where)) != 0)
       {
@@ -1350,6 +1643,12 @@ for (; cb != NULL; cb = cb->next)
 
     switch(control_type)
       {
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+      case CONTROL_BMI_RUN:
+      bmi_run = 1;
+      break;
+#endif
+      
       case CONTROL_ERROR:
       return ERROR;
 
@@ -1369,10 +1668,32 @@ for (; cb != NULL; cb = cb->next)
       smtp_enforce_sync = FALSE;
       break;
 
+#ifdef WITH_CONTENT_SCAN
+      case CONTROL_NO_MBOX_UNSPOOL:
+      no_mbox_unspool = TRUE;
+      break;
+#endif
+
       case CONTROL_NO_MULTILINE:
       no_multiline_responses = TRUE;
       break;
 
+      case CONTROL_FAKEREJECT:
+      fake_reject = TRUE;
+      if (*p == '/')
+        { 
+        uschar *pp = p + 1;
+        while (*pp != 0) pp++; 
+        fake_reject_text = expand_string(string_copyn(p+1, pp-p));
+        p = pp;
+        }
+       else
+        {
+        /* Explicitly reset to default string */
+        fake_reject_text = US"Your message has been rejected but is being kept for evaluation.\nIf it was a legitimate message, it may still be delivered to the target recipient(s).";
+        }
+      break;
+
       case CONTROL_FREEZE:
       deliver_freeze = TRUE;
       deliver_frozen_at = time(NULL);
@@ -1384,20 +1705,38 @@ for (; cb != NULL; cb = cb->next)
 
       case CONTROL_SUBMISSION:
       submission_mode = TRUE;
-      if (Ustrncmp(p, "/domain=", 8) == 0)
-        {
-        submission_domain = string_copy(p+8);
-        }
-      else if (*p != 0)
+      while (*p == '/')
+        { 
+        if (Ustrncmp(p, "/sender_retain", 14) == 0)
+          {
+          p += 14;
+          active_local_sender_retain = TRUE;
+          active_local_from_check = FALSE;   
+          }  
+        else if (Ustrncmp(p, "/domain=", 8) == 0)
+          {
+          uschar *pp = p + 8;
+          while (*pp != 0 && *pp != '/') pp++; 
+          submission_domain = string_copyn(p+8, pp-p);
+          p = pp; 
+          }
+        else break;   
+        }   
+      if (*p != 0)
         {
-        *log_msgptr = string_sprintf("syntax error in argument for "
-          "\"control\" modifier \"%s\"", arg);
+        *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg);
         return ERROR;
         }
       break;
       }
     break;
 
+#ifdef WITH_CONTENT_SCAN
+    case ACLC_DECODE:
+    rc = mime_decode(&arg);
+    break;
+#endif
+
     case ACLC_DELAY:
       {
       int delay = readconf_readtime(arg, 0, FALSE);
@@ -1416,11 +1755,20 @@ for (; cb != NULL; cb = cb->next)
           HDEBUG(D_acl)
             debug_printf("delay skipped in -bh checking mode\n");
           }
-        else sleep(delay);
+        else 
+          {
+          while (delay > 0) delay = sleep(delay);
+          } 
         }
       }
     break;
 
+#ifdef WITH_OLD_DEMIME
+    case ACLC_DEMIME:
+      rc = demime(&arg);
+    break;
+#endif
+
     case ACLC_DNSLISTS:
     rc = verify_check_dnsbl(&arg);
     break;
@@ -1500,12 +1848,42 @@ for (; cb != NULL; cb = cb->next)
       log_write(0, logbits, "%s", string_printing(s));
       }
     break;
+    
+#ifdef WITH_CONTENT_SCAN
+    case ACLC_MALWARE:
+      {
+      /* Seperate the regular expression and any optional parameters. */
+      uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size);
+      /* Run the malware backend. */
+      rc = malware(&ss);
+      /* Modify return code based upon the existance of options. */
+      while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))
+            != NULL) {
+        if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER)
+          {
+          /* FAIL so that the message is passed to the next ACL */
+          rc = FAIL;
+          }
+        }
+      }
+    break;
+
+    case ACLC_MIME_REGEX:
+      rc = mime_regex(&arg);
+    break;
+#endif
 
     case ACLC_RECIPIENTS:
     rc = match_address_list(addr->address, TRUE, TRUE, &arg, NULL, -1, 0,
       &recipient_data);
     break;
 
+#ifdef WITH_CONTENT_SCAN
+   case ACLC_REGEX:
+      rc = regex(&arg);
+    break;
+#endif
+
     case ACLC_SENDER_DOMAINS:
       {
       uschar *sdomain;
@@ -1532,6 +1910,32 @@ for (; cb != NULL; cb = cb->next)
       }
     break;
 
+#ifdef WITH_CONTENT_SCAN
+    case ACLC_SPAM:
+      {
+      /* Seperate the regular expression and any optional parameters. */
+      uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size);
+      /* Run the spam backend. */
+      rc = spam(&ss);
+      /* Modify return code based upon the existance of options. */
+      while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))
+            != NULL) {
+        if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER)
+          {
+          /* FAIL so that the message is passed to the next ACL */
+          rc = FAIL;
+          }
+        }
+      }
+    break;
+#endif
+
+#ifdef EXPERIMENTAL_SPF
+    case ACLC_SPF:
+      rc = spf_process(&arg, sender_address);
+    break;
+#endif
+
     /* If the verb is WARN, discard any user message from verification, because
     such messages are SMTP responses, not header additions. The latter come
     only from explicit "message" modifiers. */
@@ -2097,7 +2501,7 @@ else
 rc = acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr);
 
 smtp_command_argument = deliver_domain =
-  deliver_localpart = deliver_address_data = NULL;
+  deliver_localpart = deliver_address_data = sender_address_data = NULL;
 
 /* A DISCARD response is permitted only for message ACLs, excluding the PREDATA
 ACL, which is really in the middle of an SMTP command. */