[Exim] LDAP patch

Top Page
Delete this message
Reply to this message
Author: Brian Candler
Date:  
To: exim-users
Subject: [Exim] LDAP patch
The attached patch makes some changes to LDAP lookup behaviour that I'd like
to propose.

(1) If you specify multiple attributes, they are returned as space-separated
strings, quoted if necessary (the same as NIS)

e.g. ldap:///o=base?attr1,attr2?sub?(uid=fred)

       used to give:    attr1=value one, attr2=value2
       now gives:       attr1="value one" attr2=value2


(2) If you don't specify any attributes in the search, you now get them in
the tagged format as well.

e.g. ldap:///o=base??sub?(uid=fred)

       used to give:    top, value one, value2
       now gives:       objectClass=top attr1="value one" attr2=value2


The reason for these changes is so that the results can be safely parsed -
in fact, the existing ${extract{key}{val}} function does this nicely[*].
This in turn allows a single LDAP query to be reused - one query can return
the destination delivery address, the quota, and so forth.

This is NOT a backwards compatible change, so there is a compile-time option
to reverse it (or you could add two new query types in addition to ldap and
ldapm). But I don't think the old behaviour was particularly useful as it
stood, as a field which contained ',' or '=' would make the result
unparseable.

In the common case where you explicitly ask for a single attribute in your
LDAP query, the behaviour is unchanged - i.e. the result is not quoted, and
if there are multiple values they are comma-separated.

The actual quoting code is stolen directly from nisplus.c so it ought to be
OK :-)

Regards,

Brian.

[*] Note that 'extract' only gives you the first instance of an attribute if
there are multiple values.
@@ -321,10 +327,10 @@
entries. */

 for(e = ldap_first_entry(lcp->ld, result);
-    e != NULLMSG;
+    e != (void *)0;
     e = ldap_next_entry(lcp->ld, e))
   {
-  BOOL add_comma = FALSE;
+  BOOL add_space = FALSE;


DEBUG(9) debug_printf("LDAP entry loop\n");

@@ -336,7 +342,7 @@
     add_newline = TRUE;


/* Loop through the entry, grabbing attribute values. Multiple attribute
- values are separated by commas. */
+ values are separated by spaces and quoted if necessary, just like nis */

   for (attr = ldap_first_attribute(lcp->ld, e, &ber);
        attr != NULL;
@@ -352,18 +358,48 @@
           {
           DEBUG(9) debug_printf("LDAP attr loop %s:%s\n", attr, *values);


-          if (add_comma)
-            data = string_cat(data, &size, &ptr, ", ", 2);
+          if (add_space)
+            {
+#ifndef OLD_LDAP_BEHAVIOUR
+            if (attr_count != 1)
+              data = string_cat(data, &size, &ptr, " ", 1);
+            else
+#endif
+              data = string_cat(data, &size, &ptr, ", ", 2);
+            }
           else
-            add_comma = TRUE;
+            add_space = TRUE;


+#ifndef OLD_LDAP_BEHAVIOUR
+          if (attr_count != 1)
+#else
           if (attr_count > 1)
+#endif
             {
             data = string_cat(data, &size, &ptr, attr, strlen(attr));
             data = string_cat(data, &size, &ptr, "=", 1);
             }


-          data = string_cat(data, &size, &ptr, *values, strlen(*values));
+#ifndef OLD_LDAP_BEHAVIOUR
+          /* Quote the value if it contains spaces or is empty */
+          if (attr_count != 1 && ((*values)[0] == 0 || strchr(*values, ' ') != NULL))
+            {
+            char *value = *values;
+            int j;
+            int len = strlen(value);
+            data = string_cat(data, &size, &ptr, "\"", 1);
+            for (j = 0; j < len; j++)
+              {
+              if (value[j] == '\"' || value[j] == '\\')
+                data = string_cat(data, &size, &ptr, "\\", 1);
+              data = string_cat(data, &size, &ptr, value+j, 1);
+              }
+            data = string_cat(data, &size, &ptr, "\"", 1);
+            }
+          else
+#endif
+            data = string_cat(data, &size, &ptr, *values, strlen(*values));
+            
           data[ptr] = '\0';
           values++;
           attribute_found = TRUE;