Added a "connect=<time>" option to callouts, for a separate timeout
[exim.git] / src / src / acl.c
index d6a354cf2d99a9c549136a8401361d7af9cba1c3..92ebc18ec7d3e785d0339b9f975e65a63eb64d37 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/acl.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
+/* $Cambridge: exim/src/src/acl.c,v 1.5 2004/11/04 12:19:48 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -48,6 +48,12 @@ static uschar *conditions[] = { US"acl", US"authenticated", US"condition",
   US"endpass", US"hosts", US"local_parts", US"log_message", US"logwrite",
   US"message", US"recipients", US"sender_domains", US"senders", US"set",
   US"verify" };
+  
+/* ACL control names */
+
+static uschar *controls[] = { US"error", US"caseful_local_part",
+  US"caselower_local_part", US"enforce_sync", US"no_enforce_sync", US"freeze",
+  US"queue_only", US"submission", US"no_multiline"}; 
 
 /* Flags to indicate for which conditions /modifiers a string expansion is done
 at the outer level. In the other cases, expansion already occurs in the
@@ -99,8 +105,8 @@ static uschar cond_modifiers[] = {
   FALSE    /* verify */
 };
 
-/* Bit map of which conditions are not allowed at certain times. For each
-condition, there's a bitmap of dis-allowed times. */
+/* Bit map vector of which conditions are not allowed at certain times. For
+each condition, there's a bitmap of dis-allowed times. */
 
 static unsigned int cond_forbids[] = {
   0,                                               /* acl */
@@ -166,7 +172,6 @@ static unsigned int cond_forbids[] = {
   always and check in the verify function itself */
 
   0                                                /* verify */
-
 };
 
 
@@ -176,35 +181,48 @@ enum { 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 };
 
-/* Structure listing various control arguments, with their characteristics.
-The maximum "where" value controls the ACLs in which the various controls are
-permitted to occur. Specifying ACL_WHERE_RCPT limits it to just the RCPT ACL;
-specifying ACL_WHERE_NOTSMTP limits it to "message" ACLs. */
+/* 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[] = {
+  0,                                               /* error */
+  ~(1<<ACL_WHERE_RCPT),                            /* caseful_local_part */
+  ~(1<<ACL_WHERE_RCPT),                            /* caselower_local_part */
+  (1<<ACL_WHERE_NOTSMTP),                          /* enforce_sync */
+  (1<<ACL_WHERE_NOTSMTP),                          /* no_enforce_sync */
+   
+  ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* freeze */
+    (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+    (1<<ACL_WHERE_NOTSMTP)),
+     
+  ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* queue_only */
+    (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+    (1<<ACL_WHERE_NOTSMTP)),
+     
+  ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* submission */
+    (1<<ACL_WHERE_PREDATA)),                       
+     
+  (1<<ACL_WHERE_NOTSMTP)                           /* no_multiline */
+};
+
+/* Structure listing various control arguments, with their characteristics. */
 
 typedef struct control_def {
   uschar *name;
   int    value;                  /* CONTROL_xxx value */
-  int    where_max;              /* Maximum "where" value */
   BOOL   has_option;             /* Has /option(s) following */
 } control_def;
 
 static control_def controls_list[] = {
-  { US"caseful_local_part",     CONTROL_CASEFUL_LOCAL_PART,
-    ACL_WHERE_RCPT,    FALSE },
-  { US"caselower_local_part",   CONTROL_CASELOWER_LOCAL_PART,
-    ACL_WHERE_RCPT,    FALSE },
-  { US"enforce_sync",           CONTROL_ENFORCE_SYNC,
-    INT_MAX,           FALSE },
-  { US"freeze",                 CONTROL_FREEZE,
-    ACL_WHERE_NOTSMTP, FALSE },
-  { US"no_enforce_sync",        CONTROL_NO_ENFORCE_SYNC,
-    INT_MAX,           FALSE },
-  { US"no_multiline_responses", CONTROL_NO_MULTILINE,
-    INT_MAX,           FALSE },
-  { US"queue_only",             CONTROL_QUEUE_ONLY,
-    ACL_WHERE_NOTSMTP, FALSE },
-  { US"submission",             CONTROL_SUBMISSION,
-    ACL_WHERE_NOTSMTP, TRUE  }
+  { 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},
+  { US"freeze",                 CONTROL_FREEZE, FALSE},
+  { US"no_enforce_sync",        CONTROL_NO_ENFORCE_SYNC, FALSE},
+  { US"no_multiline_responses", CONTROL_NO_MULTILINE, FALSE},
+  { US"queue_only",             CONTROL_QUEUE_ONLY, FALSE},
+  { US"submission",             CONTROL_SUBMISSION, TRUE}
   };
 
 /* Enable recursion between acl_check_internal() and acl_check_condition() */
@@ -671,6 +689,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;
@@ -817,6 +836,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)
           {
@@ -885,6 +909,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);
@@ -930,7 +973,7 @@ message if giving out verification details. */
 if (verify_header_sender)
   {
   rc = verify_check_header_address(user_msgptr, log_msgptr, callout,
-    callout_overall, se_mailfrom, pm_mailfrom, verify_options);
+    callout_overall, callout_connect, se_mailfrom, pm_mailfrom, verify_options);
   if (smtp_return_error_details)
     {
     if (*user_msgptr == NULL && *log_msgptr != NULL)
@@ -994,6 +1037,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)
@@ -1011,7 +1056,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");
 
@@ -1039,7 +1084,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
@@ -1054,7 +1108,7 @@ 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;
@@ -1155,13 +1209,6 @@ if (d >= controls_list + sizeof(controls_list)/sizeof(control_def) ||
   return CONTROL_ERROR;
   }
 
-if (where > d->where_max)
-  {
-  *log_msgptr = string_sprintf("cannot use \"control=%s\" in %s ACL",
-    arg, acl_wherenames[where]);
-  return CONTROL_ERROR;
-  }
-
 *pptr = arg + len;
 return d->value;
 }
@@ -1210,6 +1257,7 @@ int rc = OK;
 for (; cb != NULL; cb = cb->next)
   {
   uschar *arg;
+  int control_type; 
 
   /* The message and log_message items set up messages to be used in
   case of rejection. They are expanded later. */
@@ -1325,7 +1373,18 @@ for (; cb != NULL; cb = cb->next)
     break;
 
     case ACLC_CONTROL:
-    switch (decode_control(arg, &p, where, log_msgptr))
+    control_type = decode_control(arg, &p, where, log_msgptr);
+
+    /* Check this control makes sense at this time */
+
+    if ((control_forbids[control_type] & (1 << where)) != 0)
+      {
+      *log_msgptr = string_sprintf("cannot use \"control=%s\" in %s ACL",
+        controls[control_type], acl_wherenames[where]);
+      return ERROR;
+      }                                                     
+
+    switch(control_type)
       {
       case CONTROL_ERROR:
       return ERROR;
@@ -1361,14 +1420,26 @@ 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;
@@ -2074,7 +2145,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. */