From 5a6c3a169bed84d29a17c6c6f87896c42565bf9d Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Wed, 19 Jun 2013 08:05:30 -0400
Subject: [PATCH] Cache LDAP's user_specific search and use vlv for better performance (#1489186)

---
 CHANGELOG                            |    1 
 program/lib/Roundcube/rcube_ldap.php |   61 ++++++++++++++++++++----------
 2 files changed, 41 insertions(+), 21 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 64da8c6..3d0d60c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- Cache LDAP's user_specific search and use vlv for better performance (#1489186)
 - LDAP: auto-detect and use VLV indices for all search operations
 - LDAP: additional group configuration options for  address books
 - LDAP: separated address book implementation from a generic LDAP wrapper class
diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php
index 36f766d..334cb24 100644
--- a/program/lib/Roundcube/rcube_ldap.php
+++ b/program/lib/Roundcube/rcube_ldap.php
@@ -265,31 +265,46 @@
 
                 $replaces = array('%dn' => '', '%dc' => $dc, '%d' => $d, '%fu' => $fu, '%u' => $u);
 
+                // Search for the dn to use to authenticate
                 if ($this->prop['search_base_dn'] && $this->prop['search_filter']) {
-                    if (!empty($this->prop['search_bind_dn']) && !empty($this->prop['search_bind_pw'])) {
-                        if (!$this->ldap->bind($this->prop['search_bind_dn'], $this->prop['search_bind_pw']))
-                            continue;  // bind failed, try neyt host
-                    }
+                    $search_bind_dn = strtr($this->prop['search_bind_dn'], $replaces);
+                    $search_base_dn = strtr($this->prop['search_base_dn'], $replaces);
+                    $search_filter  = strtr($this->prop['search_filter'], $replaces);
 
-                    // Search for the dn to use to authenticate
-                    $this->prop['search_base_dn'] = strtr($this->prop['search_base_dn'], $replaces);
-                    $this->prop['search_filter'] = strtr($this->prop['search_filter'], $replaces);
+                    $cache_key = 'DN.' . md5("$host:$search_bind_dn:$search_base_dn:$search_filter:"
+                        .$this->prop['search_bind_pw']);
 
-                    $this->_debug("S: Search {$this->prop['search_base_dn']} for {$this->prop['search_filter']}");
-
-                    // TODO: use $this->ldap->search() here
-                    $res = @ldap_search($this->ldap->conn, $this->prop['search_base_dn'], $this->prop['search_filter'], array('uid'));
-                    if ($res) {
-                        if (($entry = ldap_first_entry($this->ldap->conn, $res))
-                            && ($bind_dn = ldap_get_dn($this->ldap->conn, $entry))
-                        ) {
-                            $this->_debug("S: OK. Found $bind_dn");
-                            $dn = ldap_explode_dn($bind_dn, 1);
-                            $replaces['%dn'] = $dn[0];
-                        }
+                    if ($dn = $this->cache->get($cache_key)) {
+                        $replaces['%dn'] = $dn;
                     }
                     else {
-                        $this->_debug("S: ".ldap_error($this->ldap->conn));
+                        $ldap = $this->ldap;
+                        if (!empty($search_bind_dn) && !empty($this->prop['search_bind_pw'])) {
+                            // To protect from "Critical extension is unavailable" error
+                            // we need to use a separate LDAP connection
+                            if (!empty($this->prop['vlv'])) {
+                                $ldap = new rcube_ldap_generic($this->prop);
+                                $ldap->set_debug($this->debug);
+                                $ldap->set_cache($this->cache);
+                                if (!$ldap->connect($host)) {
+                                    continue;
+                                }
+                            }
+
+                            if (!$ldap->bind($search_bind_dn, $this->prop['search_bind_pw'])) {
+                                continue;  // bind failed, try next host
+                            }
+                        }
+
+                        $res = $ldap->search($search_base_dn, $search_filter, 'sub', array('uid'));
+                        if ($res) {
+                            $res->rewind();
+                            $replaces['%dn'] = $res->get_dn();
+                        }
+
+                        if ($ldap != $this->ldap) {
+                            $ldap->close();
+                        }
                     }
 
                     // DN not found
@@ -301,9 +316,13 @@
                                 'code' => 100, 'type' => 'ldap',
                                 'file' => __FILE__, 'line' => __LINE__,
                                 'message' => "DN not found using LDAP search."), true);
-                            return false;
+                            continue;
                         }
                     }
+
+                    if (!empty($replaces['%dn'])) {
+                        $this->cache->set($cache_key, $replaces['%dn']);
+                    }
                 }
 
                 // Replace the bind_dn and base_dn variables.

--
Gitblit v1.9.1