[exim-dev] patch for named acl variables

Top Page
Delete this message
Reply to this message
Author: Jakob Hirsch
Date:  
To: exim-dev
Subject: [exim-dev] patch for named acl variables
Hi,

with the attached patch, you can use acl_m_something and acl_c_anything.
Memory is allocated dynamically, so there is no compile time limit.

I kind of overloaded the acl set command so people can use it just as
the numbered acl vars. I did the same with the spool file format, so you
now have "-aclm m_bla 1234" in your spoolfile, so changes to externals
tools should be minimal (or zero, ideally).
I'm not sure if using store_release if a variable is redefined is
correct, but it's easy to remove that. The existing aclvar code does not
free any memory at all (which is ok as only short lived processes use it).
Oh, and I consolidated the aclvar code in spool_in.c to have no double code.

To find a variable, I use a simple linear search, which is not the most
efficient method (euphemism alert!), but as most people only use a few
variables there's is really no need for a bloated hash function with
collision handling. And many people do just the same with files...

It runs fine on a server since more than a week. And I think it's really
nice use named variables in the config, system and user filters.

Comments welcome, especially "this is soo cool" style. :)


Regards,
Jakob
diff -ur exim-4.63/src/acl.c exim-4.63-namedaclvar/src/acl.c
--- exim-4.63/src/acl.c    2006-07-31 16:19:25.000000000 +0200
+++ exim-4.63-namedaclvar/src/acl.c    2006-08-06 11:26:42.000000000 +0200
@@ -81,6 +81,7 @@
        ACLC_SENDER_DOMAINS,
        ACLC_SENDERS,
        ACLC_SET,
+       ACLC_SET_NAME,
 #ifdef WITH_CONTENT_SCAN
        ACLC_SPAM,
 #endif
@@ -131,7 +132,7 @@
 #ifdef WITH_CONTENT_SCAN
   US"regex",
 #endif
-  US"sender_domains", US"senders", US"set",
+  US"sender_domains", US"senders", US"set", US"set",
 #ifdef WITH_CONTENT_SCAN
   US"spam",
 #endif
@@ -248,6 +249,7 @@
   FALSE,   /* sender_domains */
   FALSE,   /* senders */
   TRUE,    /* set */
+  TRUE,    /* set name */
 #ifdef WITH_CONTENT_SCAN
   TRUE,    /* spam */
 #endif
@@ -306,6 +308,7 @@
   FALSE,   /* sender_domains */
   FALSE,   /* senders */
   TRUE,    /* set */
+  TRUE,    /* set name */
 #ifdef WITH_CONTENT_SCAN
   FALSE,   /* spam */
 #endif
@@ -470,7 +473,7 @@
     (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
     (1<<ACL_WHERE_STARTTLS)|(1<<ACL_WHERE_VRFY),


-  0,                                               /* set */
+  0, 0,                                            /* set, set name */


#ifdef WITH_CONTENT_SCAN
(unsigned int)
@@ -805,9 +808,9 @@

/* The "set" modifier is different in that its argument is "name=value"
rather than just a value, and we can check the validity of the name, which
- gives us a variable number to insert into the data block. */
+ gives us a variable number or name to insert into the data block. */

-  if (c == ACLC_SET)
+  if (c == ACLC_SET || c == ACLC_SET_NAME)
     {
     int offset, max, n;
     uschar *endptr;
@@ -824,17 +827,32 @@
       max = ACL_MVARS;
       }
     else goto BAD_ACL_VAR;
-
-    n = Ustrtoul(s + 5, &endptr, 10);
-    if ((*endptr != 0 && *endptr != '=' && !isspace(*endptr)) || n >= max)
+      
+    /* named acl variable? save the name (with c_ or m_) */
+    if (s[5] == '_')
       {
-      BAD_ACL_VAR:
-      *error = string_sprintf("syntax error or unrecognized name after "
-        "\"set\" in ACL modifier \"set %s\"", s);
-      return NULL;
+      int old_pool = store_pool;
+      if (s[4] == 'c') store_pool = POOL_PERM;
+      cond->type = ACLC_SET_NAME;
+      endptr = s + 4;
+      while (*endptr != 0 && *endptr != '=' && !isspace(*endptr))
+        ++endptr;
+      cond->u.varname = string_copyn(s+4, endptr-s-4);
+      store_pool = old_pool;
+      }
+    else
+      {
+      cond->type = ACLC_SET;
+      n = Ustrtoul(s + 5, &endptr, 10);
+      cond->u.varnumber = n + offset;
+      if ((*endptr != 0 && *endptr != '=' && !isspace(*endptr)) || n >= max)
+        {
+        BAD_ACL_VAR:
+        *error = string_sprintf("syntax error or unrecognized name after "
+          "\"set\" in ACL modifier \"set %s\"", s);
+        return NULL;
+        }
       }
-
-    cond->u.varnumber = n + offset;
     s = endptr;
     while (isspace(*s)) s++;
     }
@@ -2430,7 +2448,13 @@
       int t = (n < ACL_CVARS)? 'c' : 'm';
       if (n >= ACL_CVARS) n -= ACL_CVARS;
       debug_printf("acl_%c%d ", t, n);
-      lhswidth += 7;
+      lhswidth += 7; /* wrong for n>9 */
+      }
+
+    if (cb->type == ACLC_SET_NAME)
+      {
+      debug_printf("acl_%s ", cb->u.varname);
+      lhswidth += 5 + Ustrlen(cb->u.varname);
       }


     debug_printf("= %s\n", cb->arg);
@@ -2932,6 +2956,15 @@
       }
     break;


+    case ACLC_SET_NAME:
+      {
+      int old_pool = store_pool;
+      if (*cb->u.varname == 'c') store_pool = POOL_PERM;
+      acl_var_create(cb->u.varname)->value = string_copy(arg);
+      store_pool = old_pool;
+      }
+    break;
+    
     #ifdef WITH_CONTENT_SCAN
     case ACLC_SPAM:
       {
@@ -3597,4 +3630,52 @@
 return rc;
 }


+
+/*************************************************
+*     Helper functions for named ACL variables   *
+*************************************************/
+
+/* Create named acl variable or reuse existing one.
+   New variables are simply inserted at the start of the list.
+   Return the struct address. */ 
+acl_named_var *
+acl_var_create(uschar *name)
+{
+  acl_named_var **var_start, *var;
+  
+  var_start = name[0] == 'c' ? &acl_var_c : &acl_var_m;
+
+  /* see if this name already exists */
+  for(var=*var_start; var!=NULL && strcmpic(name, var->name) != 0;
+      var=var->next);
+  
+  if (var == NULL)
+    {
+    var = store_get(sizeof(acl_named_var));
+    var->next = *var_start;
+    var->name = name;
+    *var_start = var;
+    }
+  else
+    store_release(var->value);
+
+  var->value = NULL;
+  return var;
+}
+
+
+/* return named acl variable value */
+uschar *
+acl_var_get(uschar *name)
+{
+  acl_named_var *var = (*name == 'c' ? acl_var_c : acl_var_m);
+
+  /* linear search through the list */
+  for(; var!=NULL; var=var->next)
+    if (strcmpic(name, var->name) == 0)
+      return var->value;
+  
+  return US"";
+}
+
 /* End of acl.c */
diff -ur exim-4.63/src/expand.c exim-4.63-namedaclvar/src/expand.c
--- exim-4.63/src/expand.c    2006-07-31 16:19:25.000000000 +0200
+++ exim-4.63-namedaclvar/src/expand.c    2006-08-01 00:23:34.000000000 +0200
@@ -1253,6 +1253,10 @@
     max = ACL_CVARS;
     }


+  /* named acl variable */
+  if (name[5] == '_')
+    return acl_var_get(name + 4); 
+  
   if (offset >= 0)
     {
     int n = Ustrtoul(name + 5, &endptr, 10);
diff -ur exim-4.63/src/functions.h exim-4.63-namedaclvar/src/functions.h
--- exim-4.63/src/functions.h    2006-07-31 16:19:25.000000000 +0200
+++ exim-4.63-namedaclvar/src/functions.h    2006-08-01 00:36:43.000000000 +0200
@@ -40,6 +40,8 @@


 extern acl_block *acl_read(uschar *(*)(void), uschar **);
 extern int     acl_check(int, uschar *, uschar *, uschar **, uschar **);
+extern acl_named_var *acl_var_create(uschar *);
+extern uschar *acl_var_get(uschar *);
 extern uschar *auth_b64encode(uschar *, int);
 extern int     auth_b64decode(uschar *, uschar **);
 extern int     auth_call_pam(uschar *, uschar **);
diff -ur exim-4.63/src/globals.c exim-4.63-namedaclvar/src/globals.c
--- exim-4.63/src/globals.c    2006-07-31 16:19:25.000000000 +0200
+++ exim-4.63-namedaclvar/src/globals.c    2006-08-01 00:23:34.000000000 +0200
@@ -187,6 +187,8 @@
 uschar *acl_smtp_vrfy          = NULL;
 BOOL    acl_temp_details       = FALSE;
 uschar *acl_var[ACL_CVARS + ACL_MVARS];
+acl_named_var *acl_var_c       = NULL;
+acl_named_var *acl_var_m       = NULL;
 uschar *acl_verify_message     = NULL;
 string_item *acl_warn_logged   = NULL;


diff -ur exim-4.63/src/globals.h exim-4.63-namedaclvar/src/globals.h
--- exim-4.63/src/globals.h    2006-07-31 16:19:25.000000000 +0200
+++ exim-4.63-namedaclvar/src/globals.h    2006-08-01 00:23:34.000000000 +0200
@@ -129,6 +129,8 @@
 extern uschar *acl_smtp_vrfy;          /* ACL run for VRFY */
 extern BOOL    acl_temp_details;       /* TRUE to give details for 4xx error */
 extern uschar *acl_var[ACL_CVARS+ACL_MVARS]; /* User ACL variables */
+extern acl_named_var *acl_var_c;       /* named ACL connection variables */
+extern acl_named_var *acl_var_m;       /* named ACL messsage variables */
 extern uschar *acl_verify_message;     /* User message for verify failure */
 extern string_item *acl_warn_logged;   /* Logged lines */
 extern uschar *acl_wherecodes[];       /* Response codes for ACL fails */
diff -ur exim-4.63/src/smtp_in.c exim-4.63-namedaclvar/src/smtp_in.c
--- exim-4.63/src/smtp_in.c    2006-07-31 16:19:25.000000000 +0200
+++ exim-4.63-namedaclvar/src/smtp_in.c    2006-08-01 00:23:34.000000000 +0200
@@ -850,6 +850,7 @@
 spf_smtp_comment = NULL;
 #endif
 body_linecount = body_zerocount = 0;
+acl_named_var *var;


 sender_rate = sender_rate_limit = sender_rate_period = NULL;
 ratelimiters_mail = NULL;           /* Updated by ratelimit ACL condition */
@@ -858,6 +859,8 @@
 /* The message variables follow the connection variables. */


for (i = 0; i < ACL_MVARS; i++) acl_var[ACL_CVARS + i] = NULL;
+
+acl_var_m = NULL;

 /* The message body variables use malloc store. They may be set if this is
 not the first message in an SMTP session and the previous message caused them
diff -ur exim-4.63/src/spool_in.c exim-4.63-namedaclvar/src/spool_in.c
--- exim-4.63/src/spool_in.c    2006-07-31 16:19:25.000000000 +0200
+++ exim-4.63-namedaclvar/src/spool_in.c    2006-08-01 00:23:34.000000000 +0200
@@ -231,6 +231,7 @@
 long int uid, gid;
 BOOL inheader = FALSE;
 uschar *p;
+acl_named_var *var;


/* Reset all the global variables to their default values. However, there is
one exception. DO NOT change the default value of dont_deliver, because it may
@@ -238,6 +239,9 @@

for (n = 0; n < ACL_CVARS + ACL_MVARS; n++) acl_var[n] = NULL;

+acl_var_c = NULL;
+acl_var_m = NULL;
+
authenticated_id = NULL;
authenticated_sender = NULL;
allow_unqualified_recipient = FALSE;
@@ -395,30 +399,47 @@
/* Nowadays we use "-aclc" and "-aclm" for the different types of ACL
variable, because Exim may be built with different numbers of them. */

-  else if (Ustrncmp(big_buffer, "-aclc ", 6) == 0)
+  else if (Ustrncmp(big_buffer, "-aclc ", 6) == 0 ||
+           Ustrncmp(big_buffer, "-aclm ", 6) == 0)
     {
-    int index, count;
-    if (sscanf(CS big_buffer + 6, "%d %d", &index, &count) != 2)
-      goto SPOOL_FORMAT_ERROR;
-    if (index < ACL_CVARS)
+    int count;
+    if (big_buffer[6] == 'c' || big_buffer[6] == 'm') /* named acl variable */
       {
-      acl_var[index] = store_get(count + 1);
-      if (fread(acl_var[index], 1, count+1, f) < count) goto SPOOL_READ_ERROR;
-      acl_var[index][count] = 0;
+      uschar *name, *endptr;
+      int count;
+      acl_named_var *var;
+      endptr = Ustrchr(big_buffer + 6, ' ');
+      if (endptr == NULL) goto SPOOL_FORMAT_ERROR;
+      name = string_copyn(big_buffer + 6, endptr - big_buffer - 6);
+      if (sscanf(CS endptr, " %d", &count) != 1)
+        goto SPOOL_FORMAT_ERROR;
+      var = acl_var_create(name);
+      var->value = store_get(count + 1);
+      if (fread(var->value, 1, count+1, f) < count) goto SPOOL_READ_ERROR;
+      var->value[count] = 0;
       }
-    }
-
-  else if (Ustrncmp(big_buffer, "-aclm ", 6) == 0)
-    {
-    int index, count;
-    if (sscanf(CS big_buffer + 6, "%d %d", &index, &count) != 2)
-      goto SPOOL_FORMAT_ERROR;
-    if (index < ACL_MVARS)
+    else /* numbered acl variable */
       {
-      index += ACL_CVARS;
-      acl_var[index] = store_get(count + 1);
-      if (fread(acl_var[index], 1, count+1, f) < count) goto SPOOL_READ_ERROR;
-      acl_var[index][count] = 0;
+      int index, max, offset;
+      if (sscanf(CS big_buffer + 6, "%d %d", &index, &count) != 2)
+        goto SPOOL_FORMAT_ERROR;
+      if (big_buffer[4] == 'c')
+        {
+        offset = 0;
+        max = ACL_CVARS;
+        }
+      else
+        {
+        offset = ACL_CVARS;
+        max = ACL_MVARS;
+        }
+      if (index < max)
+        {
+        index += offset;
+        acl_var[index] = store_get(count + 1);
+        if (fread(acl_var[index], 1, count+1, f) < count) goto SPOOL_READ_ERROR;
+        acl_var[index][count] = 0;
+        }
       }
     }


diff -ur exim-4.63/src/spool_out.c exim-4.63-namedaclvar/src/spool_out.c
--- exim-4.63/src/spool_out.c    2006-07-31 16:19:25.000000000 +0200
+++ exim-4.63-namedaclvar/src/spool_out.c    2006-08-01 00:23:34.000000000 +0200
@@ -136,6 +136,7 @@
 struct stat statbuf;
 uschar name[256];
 uschar temp_name[256];
+acl_named_var *var;


 sprintf(CS temp_name, "%s/input/%s/hdr.%d", spool_directory, message_subdir,
   (int)getpid());
@@ -206,6 +207,14 @@
     fprintf(f, "-aclm %d %d\n%s\n", i, Ustrlen(acl_var[j]), acl_var[j]);
   }


+for (var=acl_var_c; var!=NULL; var=var->next)
+  fprintf(f, "-aclc %s %d\n%s\n", 
+          var->name, Ustrlen(var->value), var->value);
+
+for (var=acl_var_m; var!=NULL; var=var->next)
+  fprintf(f, "-aclm %s %d\n%s\n", 
+          var->name, Ustrlen(var->value), var->value);
+
 /* Now any other data that needs to be remembered. */


 fprintf(f, "-body_linecount %d\n", body_linecount);
diff -ur exim-4.63/src/structs.h exim-4.63-namedaclvar/src/structs.h
--- exim-4.63/src/structs.h    2006-07-31 16:19:25.000000000 +0200
+++ exim-4.63-namedaclvar/src/structs.h    2006-08-01 00:23:34.000000000 +0200
@@ -780,6 +780,7 @@
   union {
     BOOL negated;
     int varnumber;
+    uschar *varname;
   } u;
 } acl_condition_block;


@@ -789,4 +790,10 @@
int verb;
} acl_block;

+typedef struct acl_named_var {
+ struct acl_named_var *next;
+ uschar *name;
+ uschar *value;
+} acl_named_var;
+
/* End of structs.h */