From 56651c15c8fb076b61c41c8e649dda6b9bf4f56f Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Wed, 04 May 2011 15:31:42 -0400
Subject: [PATCH] - Stop execution when LDAP addressbook is configured but PHP's ldap functions doesn't exists

---
 program/include/rcube_ldap.php |  157 +++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 129 insertions(+), 28 deletions(-)

diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php
index 9c9973f..d9f5a10 100644
--- a/program/include/rcube_ldap.php
+++ b/program/include/rcube_ldap.php
@@ -49,6 +49,8 @@
     protected $mail_domain = '';
     protected $debug = false;
 
+    private $base_dn = '';
+    private $groups_base_dn = '';
     private $group_cache = array();
     private $group_members = array();
 
@@ -66,7 +68,7 @@
         $this->prop = $p;
 
         // check if groups are configured
-        if (is_array($p['groups']))
+        if (is_array($p['groups']) and count($p['groups']))
             $this->groups = true;
 
         // fieldmap property is given
@@ -125,8 +127,9 @@
 
         if (!function_exists('ldap_connect'))
             raise_error(array('code' => 100, 'type' => 'ldap',
-            'file' => __FILE__, 'line' => __LINE__,
-            'message' => "No ldap support in this installation of PHP"), true);
+                'file' => __FILE__, 'line' => __LINE__,
+                'message' => "No ldap support in this installation of PHP"),
+                true, true);
 
         if (is_resource($this->conn))
             return true;
@@ -162,11 +165,16 @@
         {
             $this->ready = true;
 
+            $bind_pass = $this->prop['bind_pass'];
+            $bind_user = $this->prop['bind_user'];
+            $bind_dn   = $this->prop['bind_dn'];
+            $this->base_dn   = $this->prop['base_dn'];
+
             // User specific access, generate the proper values to use.
             if ($this->prop['user_specific']) {
                 // No password set, use the session password
-                if (empty($this->prop['bind_pass'])) {
-                    $this->prop['bind_pass'] = $RCMAIL->decrypt($_SESSION['password']);
+                if (empty($bind_pass)) {
+                    $bind_pass = $RCMAIL->decrypt($_SESSION['password']);
                 }
 
                 // Get the pieces needed for variable replacement.
@@ -190,19 +198,31 @@
                         $this->_debug("S: search returned dn: $bind_dn");
 
                         if ($bind_dn) {
-                            $this->prop['bind_dn'] = $bind_dn;
                             $dn = ldap_explode_dn($bind_dn, 1);
                             $replaces['%dn'] = $dn[0];
                         }
                     }
                 }
                 // Replace the bind_dn and base_dn variables.
-                $this->prop['bind_dn'] = strtr($this->prop['bind_dn'], $replaces);
-                $this->prop['base_dn'] = strtr($this->prop['base_dn'], $replaces);
+                $bind_dn   = strtr($bind_dn, $replaces);
+                $this->base_dn   = strtr($this->base_dn, $replaces);
+
+                if (empty($bind_user)) {
+                    $bind_user = $u;
+                }
             }
 
-            if (!empty($this->prop['bind_dn']) && !empty($this->prop['bind_pass']))
-                $this->ready = $this->_bind($this->prop['bind_dn'], $this->prop['bind_pass']);
+            if (!empty($bind_pass)) {
+                if (!empty($bind_dn)) {
+                    $this->ready = $this->_bind($bind_dn, $bind_pass);
+                }
+                else if (!empty($this->prop['auth_cid'])) {
+                    $this->ready = $this->_sasl_bind($this->prop['auth_cid'], $bind_pass, $bind_user);
+                }
+                else {
+                    $this->ready = $this->_sasl_bind($bind_user, $bind_pass);
+                }
+            }
         }
         else
             raise_error(array('code' => 100, 'type' => 'ldap',
@@ -213,6 +233,58 @@
         if ($this->prop['writable']) {
             $this->readonly = false;
         } // end if
+    }
+
+
+    /**
+     * Bind connection with (SASL-) user and password
+     *
+     * @param string $authc Authentication user
+     * @param string $pass  Bind password
+     * @param string $authz Autorization user
+     *
+     * @return boolean True on success, False on error
+     */
+    private function _sasl_bind($authc, $pass, $authz=null)
+    {
+        if (!$this->conn) {
+            return false;
+        }
+
+        if (!function_exists('ldap_sasl_bind')) {
+            raise_error(array('code' => 100, 'type' => 'ldap',
+                'file' => __FILE__, 'line' => __LINE__,
+                'message' => "Unable to bind: ldap_sasl_bind() not exists"),
+                true, true);
+        }
+
+        if (!empty($authz)) {
+            $authz = 'u:' . $authz;
+        }
+
+        if (!empty($this->prop['auth_method'])) {
+            $method = $this->prop['auth_method'];
+        }
+        else {
+            $method = 'DIGEST-MD5';
+        }
+
+        $this->_debug("C: Bind [mech: $method, authc: $authc, authz: $authz] [pass: $pass]");
+
+        if (ldap_sasl_bind($this->conn, NULL, $pass, $method, NULL, $authc, $authz)) {
+            $this->_debug("S: OK");
+            return true;
+        }
+
+        $this->_debug("S: ".ldap_error($this->conn));
+
+        raise_error(array(
+            'code' => ldap_errno($this->conn), 'type' => 'ldap',
+            'file' => __FILE__, 'line' => __LINE__,
+            'message' => "Bind failed for authcid=$authc ".ldap_error($this->conn)),
+            true);
+
+        return false;
     }
 
 
@@ -238,11 +310,11 @@
 
         $this->_debug("S: ".ldap_error($this->conn));
 
-        $error =  array(
-                'code' => ldap_errno($this->conn), 'type' => 'ldap',
-                'file' => __FILE__, 'line' => __LINE__,
-                'message' => "Bind failed for dn=$dn: ".ldap_error($this->conn));
-        raise_error($error,true);
+        raise_error(array(
+            'code' => ldap_errno($this->conn), 'type' => 'ldap',
+            'file' => __FILE__, 'line' => __LINE__,
+            'message' => "Bind failed for dn=$dn: ".ldap_error($this->conn)),
+            true);
 
         return false;
     }
@@ -407,6 +479,17 @@
 
         $filter = '(|';
         $wc = !$strict && $this->prop['fuzzy_search'] ? '*' : '';
+        if ($fields != '*')
+        {
+            // search_fields are required for fulltext search
+            if (!$this->prop['search_fields'])
+            {
+                $this->set_error(self::ERROR_SEARCH, 'nofulltextsearch');
+                $this->result = new rcube_result_set();
+                return $this->result;
+            }
+        }
+        
         if (is_array($this->prop['search_fields']))
         {
             foreach ($this->prop['search_fields'] as $k => $field)
@@ -563,7 +646,7 @@
         }
 
         // Build the new entries DN.
-        $dn = $this->prop['LDAP_rdn'].'='.$this->_quote_string($newentry[$this->prop['LDAP_rdn']], true).','.$this->prop['base_dn'];
+        $dn = $this->prop['LDAP_rdn'].'='.$this->_quote_string($newentry[$this->prop['LDAP_rdn']], true).','.$this->base_dn;
 
         $this->_debug("C: Add [dn: $dn]: ".print_r($newentry, true));
 
@@ -647,7 +730,7 @@
             if ($replacedata[$this->prop['LDAP_rdn']]) {
                 $newdn = $this->prop['LDAP_rdn'].'='
                     .$this->_quote_string($replacedata[$this->prop['LDAP_rdn']], true)
-                    .','.$this->prop['base_dn'];
+                    .','.$this->base_dn;
                 if ($dn != $newdn) {
                     $newrdn = $this->prop['LDAP_rdn'].'='
                     .$this->_quote_string($replacedata[$this->prop['LDAP_rdn']], true);
@@ -756,7 +839,7 @@
 
             $this->_debug("C: Search [".$filter."]");
 
-            if ($this->ldap_result = @$function($this->conn, $this->prop['base_dn'], $filter,
+            if ($this->ldap_result = @$function($this->conn, $this->base_dn, $filter,
                 array_values($this->fieldmap), 0, (int) $this->prop['sizelimit'], (int) $this->prop['timelimit']))
             {
                 $this->_debug("S: ".ldap_count_entries($this->conn, $this->ldap_result)." record(s)");
@@ -891,11 +974,27 @@
      */
     function list_groups($search = null)
     {
+        global $RCMAIL;
+
         if (!$this->groups)
             return array();
 
-        $base_dn = $this->prop['groups']['base_dn'];
-        $filter = '(objectClass=groupOfNames)';
+        $this->groups_base_dn = ($this->prop['groups']['base_dn']) ?
+                $this->prop['groups']['base_dn'] : $this->base_dn;
+
+        // replace user specific dn
+        if ($this->prop['user_specific'])
+        {
+            $fu = $RCMAIL->user->get_username();
+            list($u, $d) = explode('@', $fu);
+            $dc = 'dc='.strtr($d, array('.' => ',dc='));
+            $replaces = array('%dc' => $dc, '%d' => $d, '%fu' => $fu, '%u' => $u);
+
+            $this->groups_base_dn = strtr($this->groups_base_dn, $replaces);;
+        }
+
+        $base_dn = $this->groups_base_dn;
+        $filter = $this->prop['groups']['filter'];
 
         $res = ldap_search($this->conn, $base_dn, $filter, array('cn','member'));
         if ($res === false)
@@ -934,12 +1033,12 @@
         if (!$this->group_cache)
             $this->list_groups();
 
-        $base_dn = $this->prop['groups']['base_dn'];
+        $base_dn = $this->groups_base_dn;
         $new_dn = "cn=$group_name,$base_dn";
         $new_gid = base64_encode($group_name);
 
         $new_entry = array(
-            'objectClass' => array('top', 'groupOfNames'),
+            'objectClass' => $this->prop['groups']['object_classes'],
             'cn' => $group_name,
             'member' => '',
         );
@@ -965,7 +1064,7 @@
         if (!$this->group_cache)
             $this->list_groups();
 
-        $base_dn = $this->prop['groups']['base_dn'];
+        $base_dn = $this->groups_base_dn;
         $group_name = $this->group_cache[$group_id]['name'];
 
         $del_dn = "cn=$group_name,$base_dn";
@@ -984,17 +1083,19 @@
      *
      * @param string Group identifier
      * @param string New name to set for this group
+     * @param string New group identifier (if changed, otherwise don't set)
      * @return boolean New name on success, false if no data was changed
      */
-    function rename_group($group_id, $new_name)
+    function rename_group($group_id, $new_name, &$new_gid)
     {
         if (!$this->group_cache)
             $this->list_groups();
 
-        $base_dn = $this->prop['groups']['base_dn'];
+        $base_dn = $this->groups_base_dn;
         $group_name = $this->group_cache[$group_id]['name'];
         $old_dn = "cn=$group_name,$base_dn";
         $new_rdn = "cn=$new_name";
+        $new_gid = base64_encode($new_name);
 
         $res = ldap_rename($this->conn, $old_dn, $new_rdn, NULL, TRUE);
         if ($res === false)
@@ -1018,7 +1119,7 @@
         if (!$this->group_cache)
             $this->list_groups();
 
-        $base_dn = $this->prop['groups']['base_dn'];
+        $base_dn = $this->groups_base_dn;
         $group_name = $this->group_cache[$group_id]['name'];
         $group_dn = "cn=$group_name,$base_dn";
 
@@ -1048,7 +1149,7 @@
         if (!$this->group_cache)
             $this->list_groups();
 
-        $base_dn = $this->prop['groups']['base_dn'];
+        $base_dn = $this->groups_base_dn;
         $group_name = $this->group_cache[$group_id]['name'];
         $group_dn = "cn=$group_name,$base_dn";
 
@@ -1079,7 +1180,7 @@
         if (!$this->groups)
             return array();
 
-        $base_dn = $this->prop['groups']['base_dn'];
+        $base_dn = $this->groups_base_dn;
         $contact_dn = base64_decode($contact_id);
         $filter = "(member=$contact_dn)";
 

--
Gitblit v1.9.1