From 83eeec6c0665ff18881ba05743b7368f9fb1c2f7 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 3 Mar 2015 12:53:05 +0100 Subject: Add utility function to compose a full-text-like LDAP search filter --- program/lib/Roundcube/rcube_ldap_generic.php | 42 +++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'program/lib/Roundcube/rcube_ldap_generic.php') diff --git a/program/lib/Roundcube/rcube_ldap_generic.php b/program/lib/Roundcube/rcube_ldap_generic.php index a76ad6d06..40adbedbe 100644 --- a/program/lib/Roundcube/rcube_ldap_generic.php +++ b/program/lib/Roundcube/rcube_ldap_generic.php @@ -6,7 +6,7 @@ | | | This file is part of the Roundcube Webmail client | | Copyright (C) 2006-2014, The Roundcube Dev Team | - | Copyright (C) 2012-2014, Kolab Systems AG | + | Copyright (C) 2012-2015, Kolab Systems AG | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -316,6 +316,46 @@ class rcube_ldap_generic extends Net_LDAP3 return $rec; } + + /** + * Compose an LDAP filter string matching all words from the search string + * in the given list of attributes. + * + * @param string $value Search value + * @param mixed $attrs List of LDAP attributes to search + * @param int $mode Matching mode: + * 0 - partial (*abc*), + * 1 - strict (=), + * 2 - prefix (abc*) + * @return string LDAP filter + */ + public static function fulltext_search_filter($value, $attributes, $mode = 1) + { + if (empty($attributes)) { + $attributes = array('cn'); + } + + $groups = array(); + $words = rcube_utils::tokenize_string($value, 1); + + // set wildcards + $wp = $ws = ''; + if ($mode != 1) { + $ws = '*'; + $wp = !$mode ? '*' : ''; + } + + // search each word in all listed attributes + foreach ($words as $word) { + $parts = array(); + foreach ($attributes as $attr) { + $parts[] = "($attr=$wp" . self::quote_string($word) . "$ws)"; + } + $groups[] = '(|' . join('', $parts) . ')'; + } + + return empty($groups) ? '' : '(&' . join('', $groups) . ')'; + } } // for backward compat. -- cgit v1.2.3 From 36ee2c8427298fc8735fe547d11c7e203fb3ca99 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 3 Mar 2015 14:53:02 +0100 Subject: Improve LDAP search by ignoring words order in fuzzy substring matching mode --- program/lib/Roundcube/rcube_ldap.php | 66 +++++++++++++++++----------- program/lib/Roundcube/rcube_ldap_generic.php | 5 ++- 2 files changed, 44 insertions(+), 27 deletions(-) (limited to 'program/lib/Roundcube/rcube_ldap_generic.php') diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php index 87dcb2bf9..aad0090f8 100644 --- a/program/lib/Roundcube/rcube_ldap.php +++ b/program/lib/Roundcube/rcube_ldap.php @@ -792,33 +792,24 @@ class rcube_ldap extends rcube_addressbook return $this->result; } - // use AND operator for advanced searches - $filter = is_array($value) ? '(&' : '(|'; - // set wildcards - $wp = $ws = ''; - if (!empty($this->prop['fuzzy_search']) && $mode != 1) { - $ws = '*'; - if (!$mode) { - $wp = '*'; - } - } - - if ($fields == '*') { - // search_fields are required for fulltext search - if (empty($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 $field) { - $filter .= "($field=$wp" . rcube_ldap_generic::quote_string($value) . "$ws)"; + // advanced per-attribute search + if (is_array($value)) { + // use AND operator for advanced searches + $filter = '(&'; + + // set wildcards + $wp = $ws = ''; + if (!empty($this->prop['fuzzy_search']) && $mode != 1) { + $ws = '*'; + if (!$mode) { + $wp = '*'; } } - } - else { + foreach ((array)$fields as $idx => $field) { - $val = is_array($value) ? $value[$idx] : $value; + $val = $value[$idx]; + if (!strlen($val)) + continue; if ($attrs = $this->_map_field($field)) { if (count($attrs) > 1) $filter .= '(|'; @@ -828,8 +819,33 @@ class rcube_ldap extends rcube_addressbook $filter .= ')'; } } + + $filter .= ')'; + } + else { + if ($fields == '*') { + // search_fields are required for fulltext search + if (empty($this->prop['search_fields'])) { + $this->set_error(self::ERROR_SEARCH, 'nofulltextsearch'); + $this->result = new rcube_result_set(); + return $this->result; + } + $attributes = (array)$this->prop['search_fields']; + } + else { + // map address book fields into ldap attributes + $me = $this; + $attributes = array(); + array_walk($fields, function($field) use ($me, &$attributes) { + if ($this->coltypes[$field] && ($attrs = (array)$this->coltypes[$field]['attributes'])) { + $attributes = array_merge($attributes, $attrs); + } + }); + } + + // compose a full-text-like search filter + $filter = rcube_ldap_generic::fulltext_search_filter($value, $attributes, $mode); } - $filter .= ')'; // add required (non empty) fields filter $req_filter = ''; diff --git a/program/lib/Roundcube/rcube_ldap_generic.php b/program/lib/Roundcube/rcube_ldap_generic.php index 40adbedbe..abe16760d 100644 --- a/program/lib/Roundcube/rcube_ldap_generic.php +++ b/program/lib/Roundcube/rcube_ldap_generic.php @@ -336,7 +336,8 @@ class rcube_ldap_generic extends Net_LDAP3 } $groups = array(); - $words = rcube_utils::tokenize_string($value, 1); + $value = str_replace('*', '', $value); + $words = $mode == 0 ? rcube_utils::tokenize_string($value, 1) : array($value); // set wildcards $wp = $ws = ''; @@ -354,7 +355,7 @@ class rcube_ldap_generic extends Net_LDAP3 $groups[] = '(|' . join('', $parts) . ')'; } - return empty($groups) ? '' : '(&' . join('', $groups) . ')'; + return count($groups) > 1 ? '(&' . join('', $groups) . ')' : join('', $groups); } } -- cgit v1.2.3