From b1f0846727331f58342e9cfdd02df03ea8f15181 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Fri, 07 Oct 2011 04:33:24 -0400
Subject: [PATCH] - Make sure LDAP name fields aren't arrays (#1488108)

---
 program/include/rcube_ldap.php |  139 +++++++++++++++++++++++++++++++++++----------
 1 files changed, 107 insertions(+), 32 deletions(-)

diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php
index c6ca88e..a3f6dc5 100644
--- a/program/include/rcube_ldap.php
+++ b/program/include/rcube_ldap.php
@@ -53,8 +53,9 @@
 
     private $base_dn = '';
     private $groups_base_dn = '';
-    private $group_cache = array();
+    private $group_url = null;
     private $group_members = array();
+    private $cache;
 
     private $vlv_active = false;
     private $vlv_count = 0;
@@ -72,6 +73,9 @@
     {
         $this->prop = $p;
 
+        if (isset($p['searchonly']))
+            $this->searchonly = $p['searchonly'];
+
         // check if groups are configured
         if (is_array($p['groups']) && count($p['groups'])) {
             $this->groups = true;
@@ -80,6 +84,9 @@
                 $this->prop['member_attr'] = strtolower($p['groups']['member_attr']);
             else if (empty($p['member_attr']))
                 $this->prop['member_attr'] = 'member';
+            // set default name attribute to cn
+            if (empty($this->prop['groups']['name_attr']))
+                $this->prop['groups']['name_attr'] = 'cn';
         }
 
         // fieldmap property is given
@@ -124,6 +131,11 @@
         $this->sort_col    = is_array($p['sort']) ? $p['sort'][0] : $p['sort'];
         $this->debug       = $debug;
         $this->mail_domain = $mail_domain;
+
+        // initialize cache
+        $rcmail = rcmail::get_instance();
+        $this->cache = $rcmail->get_cache('LDAP.' . asciiwords($this->prop['name']), 'db', 600);
+        $this->cache->expunge();
 
         $this->_connect();
     }
@@ -429,11 +441,30 @@
      */
     function list_records($cols=null, $subset=0)
     {
+        if ($this->prop['searchonly'] && empty($this->filter) && !$this->group_id)
+        {
+            $this->result = new rcube_result_set(0);
+            $this->result->searchonly = true;
+            return $this->result;
+        }
+
         // add general filter to query
         if (!empty($this->prop['filter']) && empty($this->filter))
         {
             $filter = $this->prop['filter'];
             $this->set_search_set($filter);
+        }
+
+        // query URL is given by the selected group
+        if ($this->group_id && $this->group_url)
+        {
+            // extract components from url
+            if (preg_match('!ldap:///([^\?]+)\?\?(\w+)\?(.*)$!', $this->group_url, $m))
+            {
+                $this->base_dn = $m[1];
+                $this->prop['scope'] = $m[2];
+                $this->filter = $m[3];
+            }
         }
 
         // exec LDAP search if no result resource is stored
@@ -460,7 +491,7 @@
             $entries = ldap_get_entries($this->conn, $this->ldap_result);
 
             // filtering for group members
-            if ($this->groups and $this->group_id)
+            if ($this->groups && $this->group_id && !$this->group_url)
             {
                 $count = 0;
                 $members = array();
@@ -922,7 +953,7 @@
             $this->_debug("C: Search [$filter]");
 
             // when using VLV, we get the total count by...
-            if (!$count && $function != 'ldap_read' && $this->prop['vlv']) {
+            if (!$count && $function != 'ldap_read' && $this->prop['vlv'] && !$this->group_id) {
                 // ...either reading numSubOrdinates attribute
                 if ($this->prop['numsub_filter'] && ($result_count = @$function($this->conn, $this->base_dn, $this->prop['numsub_filter'], array('numSubOrdinates'), 0, 0, 0))) {
                     $counts = ldap_get_entries($this->conn, $result_count);
@@ -1000,6 +1031,11 @@
                 else
                     $out[$rf] = $value;
             }
+
+            // Make sure name fields aren't arrays (#1488108)
+            if (is_array($out[$rf]) && in_array($rf, array('name', 'surname', 'firstname', 'middlename', 'nickname'))) {
+                $out[$rf] = $out[$rf][0];
+            }
         }
 
         return $out;
@@ -1075,10 +1111,10 @@
     {
         if ($group_id)
         {
-            if (!$this->group_cache)
-                $this->list_groups();
+            if (!($group_cache = $this->cache->get('groups')))
+                $group_cache = $this->list_groups();
 
-            $cache_members = $this->group_cache[$group_id]['members'];
+            $cache_members = $group_cache[$group_id]['members'];
 
             $members = array();
             for ($i=0; $i<$cache_members["count"]; $i++)
@@ -1087,10 +1123,15 @@
                     $members[self::dn_encode($cache_members[$i])] = 1;
             }
             $this->group_members = $members;
+            $this->group_url = $group_cache[$group_id]['member_url'];
             $this->group_id = $group_id;
         }
         else
+        {
             $this->group_id = 0;
+            $this->group_url = null;
+            $this->group_members = array();
+        }
     }
 
     /**
@@ -1106,10 +1147,11 @@
 
         $base_dn = $this->groups_base_dn;
         $filter = $this->prop['groups']['filter'];
+        $name_attr = $this->prop['groups']['name_attr'];
 
         $this->_debug("C: Search [$filter][dn: $base_dn]");
 
-        $res = @ldap_search($this->conn, $base_dn, $filter, array('cn', $this->prop['member_attr']));
+        $res = @ldap_search($this->conn, $base_dn, $filter);
         if ($res === false)
         {
             $this->_debug("S: ".ldap_error($this->conn));
@@ -1123,18 +1165,45 @@
         $group_sortnames = array();
         for ($i=0; $i<$ldap_data["count"]; $i++)
         {
-            $group_name = $ldap_data[$i]['cn'][0];
+            $group_name = $ldap_data[$i][$name_attr][0];
             if (!$search || strstr(strtolower($group_name), strtolower($search)))
             {
                 $group_id = self::dn_encode($group_name);
                 $groups[$group_id]['ID'] = $group_id;
                 $groups[$group_id]['name'] = $group_name;
-                $groups[$group_id]['members'] = $ldap_data[$i][$this->prop['member_attr']];
+
+                // default behavior: check members attribute
+                if ($this->prop['member_attr']) {
+                    $groups[$group_id]['members'] = $ldap_data[$i][$this->prop['member_attr']];
+                    $groups[$group_id]['member_attr'] = $this->prop['member_attr'];
+                }
+
+                // check objectClass attributes of group and act accordingly
+                for ($j=0; $j < $ldap_data[$i]['objectclass']['count']; $j++) {
+                    switch (strtolower($ldap_data[$i]['objectclass'][$j])) {
+                        case 'groupofnames':
+                            $groups[$group_id]['members'] = $ldap_data[$i]['members'];
+                            $groups[$group_id]['member_attr'] = 'member';
+                            break;
+
+                        case 'groupofuniquenames':
+                            $groups[$group_id]['members'] = $ldap_data[$i]['uniqueMember'];
+                            $groups[$group_id]['member_attr'] = 'uniqueMember';
+                            break;
+
+                        case 'groupofurls':
+                            $groups[$group_id]['member_url'] = $ldap_data[$i]['memberurl'][0];
+                            break;
+                    }
+                }
+
                 $group_sortnames[] = strtolower($group_name);
             }
         }
         array_multisort($group_sortnames, SORT_ASC, SORT_STRING, $groups);
-        $this->group_cache = $groups;
+
+        // cache this
+        $this->cache->set('groups', $groups);
 
         return $groups;
     }
@@ -1147,17 +1216,14 @@
      */
     function create_group($group_name)
     {
-        if (!$this->group_cache)
-            $this->list_groups();
-
         $base_dn = $this->groups_base_dn;
         $new_dn = "cn=$group_name,$base_dn";
         $new_gid = self::dn_encode($group_name);
+        $name_attr = $this->prop['groups']['name_attr'];
 
         $new_entry = array(
             'objectClass' => $this->prop['groups']['object_classes'],
-            'cn' => $group_name,
-            $this->prop['member_attr'] => '',
+            $name_attr => $group_name,
         );
 
         $this->_debug("C: Add [dn: $new_dn]: ".print_r($new_entry, true));
@@ -1171,6 +1237,7 @@
         }
 
         $this->_debug("S: OK");
+        $this->cache->remove('groups');
 
         return array('id' => $new_gid, 'name' => $group_name);
     }
@@ -1183,11 +1250,11 @@
      */
     function delete_group($group_id)
     {
-        if (!$this->group_cache)
-            $this->list_groups();
+        if (!($group_cache = $this->cache->get('groups')))
+            $group_cache = $this->list_groups();
 
         $base_dn = $this->groups_base_dn;
-        $group_name = $this->group_cache[$group_id]['name'];
+        $group_name = $group_cache[$group_id]['name'];
         $del_dn = "cn=$group_name,$base_dn";
 
         $this->_debug("C: Delete [dn: $del_dn]");
@@ -1201,6 +1268,7 @@
         }
 
         $this->_debug("S: OK");
+        $this->cache->remove('groups');
 
         return true;
     }
@@ -1215,11 +1283,11 @@
      */
     function rename_group($group_id, $new_name, &$new_gid)
     {
-        if (!$this->group_cache)
-            $this->list_groups();
+        if (!($group_cache = $this->cache->get('groups')))
+            $group_cache = $this->list_groups();
 
         $base_dn = $this->groups_base_dn;
-        $group_name = $this->group_cache[$group_id]['name'];
+        $group_name = $group_cache[$group_id]['name'];
         $old_dn = "cn=$group_name,$base_dn";
         $new_rdn = "cn=$new_name";
         $new_gid = self::dn_encode($new_name);
@@ -1235,6 +1303,7 @@
         }
 
         $this->_debug("S: OK");
+        $this->cache->remove('groups');
 
         return $new_name;
     }
@@ -1248,12 +1317,12 @@
      */
     function add_to_group($group_id, $contact_ids)
     {
-        if (!$this->group_cache)
-            $this->list_groups();
+        if (!($group_cache = $this->cache->get('groups')))
+            $group_cache = $this->list_groups();
 
         $base_dn     = $this->groups_base_dn;
-        $group_name  = $this->group_cache[$group_id]['name'];
-        $member_attr = $this->prop['member_attr'];
+        $group_name  = $group_cache[$group_id]['name'];
+        $member_attr = $group_cache[$group_id]['member_attr'];
         $group_dn    = "cn=$group_name,$base_dn";
 
         $new_attrs = array();
@@ -1271,6 +1340,7 @@
         }
 
         $this->_debug("S: OK");
+        $this->cache->remove('groups');
 
         return count($new_attrs['member']);
     }
@@ -1284,12 +1354,12 @@
      */
     function remove_from_group($group_id, $contact_ids)
     {
-        if (!$this->group_cache)
-            $this->list_groups();
+        if (!($group_cache = $this->cache->get('groups')))
+            $group_cache = $this->list_groups();
 
         $base_dn     = $this->groups_base_dn;
-        $group_name  = $this->group_cache[$group_id]['name'];
-        $member_attr = $this->prop['member_attr'];
+        $group_name  = $group_cache[$group_id]['name'];
+        $member_attr = $group_cache[$group_id]['member_attr'];
         $group_dn    = "cn=$group_name,$base_dn";
 
         $del_attrs = array();
@@ -1307,6 +1377,7 @@
         }
 
         $this->_debug("S: OK");
+        $this->cache->remove('groups');
 
         return count($del_attrs['member']);
     }
@@ -1326,12 +1397,16 @@
 
         $base_dn     = $this->groups_base_dn;
         $contact_dn  = self::dn_decode($contact_id);
+        $name_attr   = $this->prop['groups']['name_attr'];
         $member_attr = $this->prop['member_attr'];
-        $filter      = strtr("($member_attr=$contact_dn)", array('\\' => '\\\\'));
+        $add_filter  = '';
+        if ($member_attr != 'member' && $member_attr != 'uniqueMember')
+            $add_filter = "($member_attr=$contact_dn)";
+        $filter = strtr("(|(member=$contact_dn)(uniqueMember=$contact_dn)$add_filter)", array('\\' => '\\\\'));
 
         $this->_debug("C: Search [$filter][dn: $base_dn]");
 
-        $res = @ldap_search($this->conn, $base_dn, $filter, array('cn'));
+        $res = @ldap_search($this->conn, $base_dn, $filter, array($name_attr));
         if ($res === false)
         {
             $this->_debug("S: ".ldap_error($this->conn));
@@ -1343,7 +1418,7 @@
         $groups = array();
         for ($i=0; $i<$ldap_data["count"]; $i++)
         {
-            $group_name = $ldap_data[$i]['cn'][0];
+            $group_name = $ldap_data[$i][$name_attr][0];
             $group_id = self::dn_encode($group_name);
             $groups[$group_id] = $group_id;
         }

--
Gitblit v1.9.1