summaryrefslogtreecommitdiff
path: root/program
diff options
context:
space:
mode:
Diffstat (limited to 'program')
-rw-r--r--program/include/rcmail.php2
-rw-r--r--program/lib/Roundcube/rcube_ldap.php244
2 files changed, 153 insertions, 93 deletions
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 70dba4192..a149db1f6 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -287,7 +287,7 @@ class rcmail extends rcube
$list[$id] = array(
'id' => $id,
'name' => html::quote($prop['name']),
- 'groups' => is_array($prop['groups']),
+ 'groups' => !empty($prop['groups']) || !empty($prop['group_filters']),
'readonly' => !$prop['writable'],
'hidden' => $prop['hidden'],
'autocomplete' => in_array($id, $autocomplete)
diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php
index 80b85d45b..e54e65984 100644
--- a/program/lib/Roundcube/rcube_ldap.php
+++ b/program/lib/Roundcube/rcube_ldap.php
@@ -38,7 +38,7 @@ class rcube_ldap extends rcube_addressbook
/** private properties */
protected $conn;
protected $prop = array();
- protected $fieldmap = array();
+ protected $fieldmap = array('_objclass' => 'objectclass');
protected $sub_filter;
protected $filter = '';
protected $result = null;
@@ -82,6 +82,12 @@ class rcube_ldap extends rcube_addressbook
$this->prop['groups']['name_attr'] = 'cn';
if (empty($this->prop['groups']['scope']))
$this->prop['groups']['scope'] = 'sub';
+
+ // add group name attrib to fieldmap in order to have it fetched
+ $this->fieldmap['_groupname'] = $this->prop['groups']['name_attr'];
+ }
+ else if (is_array($p['group_filters']) && count($p['group_filters'])) {
+ $this->groups = true;
}
// fieldmap property is given
@@ -549,13 +555,15 @@ class rcube_ldap extends rcube_addressbook
}
else
{
+ $prop = $this->group_id ? $this->group_data : $this->prop;
+
// add general filter to query
- if (!empty($this->prop['filter']) && empty($this->filter))
- $this->set_search_set($this->prop['filter']);
+ if (!empty($prop['filter']) && empty($this->filter))
+ $this->set_search_set($prop['filter']);
// exec LDAP search if no result resource is stored
if ($this->conn && !$this->ldap_result)
- $this->_exec_search();
+ $this->_exec_search($prop);
// count contacts for this user
$this->result = $this->count();
@@ -564,7 +572,7 @@ class rcube_ldap extends rcube_addressbook
if ($this->ldap_result && $this->result->count > 0)
{
// sorting still on the ldap server
- if ($this->sort_col && $this->prop['scope'] !== 'base' && !$this->vlv_active)
+ if ($this->sort_col && $prop['scope'] !== 'base' && !$this->vlv_active)
ldap_sort($this->conn, $this->ldap_result, $this->sort_col);
// get all entries from the ldap server
@@ -599,6 +607,7 @@ class rcube_ldap extends rcube_addressbook
// fetch group object
if (empty($entries)) {
+ $this->_debug("C: Read Group [dn: $dn]");
$result = @ldap_read($this->conn, $dn, '(objectClass=*)', array('dn','objectClass','member','uniqueMember','memberURL'));
if ($result === false)
{
@@ -657,21 +666,6 @@ class rcube_ldap extends rcube_addressbook
if (empty($entry[$attr]))
return $group_members;
- // add group record to cache if it isn't yet there
- $group_id = self::dn_encode($dn);
- $group_cache = $this->cache->get('groups');
- if (!$group_cache[$group_id]) {
- $name_attr = $this->prop['groups']['name_attr'];
- $group_name = is_array($ldap_data[$i][$name_attr]) ? $ldap_data[$i][$name_attr][0] : $ldap_data[$i][$name_attr];
-
- $group_cache[$group_id]['ID'] = $group_id;
- $group_cache[$group_id]['dn'] = $dn;
- $group_cache[$group_id]['name'] = $group_name;
- $group_cache[$group_id]['member_attr'] = $this->get_group_member_attr($entry[$i]['objectclass']);
-
- $this->cache->set('groups', $group_cache);
- }
-
// read these attributes for all members
$attrib = $count ? array('dn') : array_values($this->fieldmap);
$attrib[] = 'objectClass';
@@ -726,26 +720,15 @@ class rcube_ldap extends rcube_addressbook
// add search filter if any
$filter = $this->filter ? '(&(' . $m[3] . ')(' . $this->filter . '))' : $m[3];
- $func = $m[2] == 'sub' ? 'ldap_search' : ($m[2] == 'base' ? 'ldap_read' : 'ldap_list');
-
- $attrib = $count ? array('dn') : array_values($this->fieldmap);
- if ($result = @$func($this->conn, $m[1], $filter,
- $attrib, 0, (int)$this->prop['sizelimit'], (int)$this->prop['timelimit'])
- ) {
- $this->_debug("S: ".ldap_count_entries($this->conn, $result)." record(s) for ".$m[1]);
- }
- else {
- $this->_debug("S: ".ldap_error($this->conn));
- return $group_members;
- }
-
- $entries = @ldap_get_entries($this->conn, $result);
- for ($j = 0; $j < $entries['count']; $j++)
- {
- if ($nested_group_members = $this->list_group_members($entries[$j]['dn'], $count))
- $group_members = array_merge($group_members, $nested_group_members);
- else
- $group_members[] = $entries[$j];
+ $attrs = $count ? array('dn') : array_values($this->fieldmap);
+ if ($result = $this->ldap_search($m[1], $filter, $m[2], $attrs, $this->group_data)) {
+ $entries = @ldap_get_entries($this->conn, $result);
+ for ($j = 0; $j < $entries['count']; $j++) {
+ if ($nested_group_members = $this->list_group_members($entries[$j]['dn'], $count))
+ $group_members = array_merge($group_members, $nested_group_members);
+ else
+ $group_members[] = $entries[$j];
+ }
}
}
@@ -910,7 +893,7 @@ class rcube_ldap extends rcube_addressbook
// set filter string and execute search
$this->set_search_set($filter);
- $this->_exec_search();
+ $this->_exec_search($this->prop);
if ($select)
$this->list_records();
@@ -936,13 +919,15 @@ class rcube_ldap extends rcube_addressbook
$count = count($this->list_group_members($this->group_data['dn'], true));
}
else if ($this->conn) {
+ $prop = $this->group_id ? $this->group_data : $this->prop;
+
// We have a connection but no result set, attempt to get one.
if (empty($this->filter)) {
// The filter is not set, set it.
$this->filter = $this->prop['filter'];
}
- $count = (int) $this->_exec_search(true);
+ $count = (int) $this->_exec_search($prop, true);
}
return new rcube_result_set($count, ($this->list_page-1) * $this->page_size);
@@ -1434,57 +1419,35 @@ class rcube_ldap extends rcube_addressbook
/**
* Execute the LDAP search based on the stored credentials
*/
- private function _exec_search($count = false)
+ private function _exec_search($prop, $count = false)
{
if ($this->ready)
{
- $filter = $this->filter ? $this->filter : '(objectclass=*)';
- $function = $this->_scope2func($this->prop['scope'], $ns_function);
-
- $this->_debug("C: Search [$filter][dn: $this->base_dn]");
+ $function = $this->_scope2func($prop['scope'], $ns_function);
+ $base_dn = $this->group_id && $prop['base_dn'] ? $prop['base_dn'] : $this->base_dn;
// when using VLV, we get the total count by...
- if (!$count && $function != 'ldap_read' && $this->prop['vlv'] && !$this->group_id) {
+ if (!$count && $function != 'ldap_read' && $prop['vlv']) {
// ...either reading numSubOrdinates attribute
- if ($this->prop['numsub_filter'] && ($result_count = @$ns_function($this->conn, $this->base_dn, $this->prop['numsub_filter'], array('numSubOrdinates'), 0, 0, 0))) {
+ if ($prop['numsub_filter'] && ($result_count = @$ns_function($this->conn, $base_dn, $prop['numsub_filter'], array('numSubOrdinates'), 0, 0, 0))) {
$counts = ldap_get_entries($this->conn, $result_count);
for ($this->vlv_count = $j = 0; $j < $counts['count']; $j++)
$this->vlv_count += $counts[$j]['numsubordinates'][0];
$this->_debug("D: total numsubordinates = " . $this->vlv_count);
}
else if (!function_exists('ldap_parse_virtuallist_control')) // ...or by fetching all records dn and count them
- $this->vlv_count = $this->_exec_search(true);
-
- $this->vlv_active = $this->_vlv_set_controls($this->prop, $this->list_page, $this->page_size);
+ $this->vlv_count = $this->_exec_search($prop, true);
}
- // only fetch dn for count (should keep the payload low)
- $attrs = $count ? array('dn') : array_values($this->fieldmap);
- if ($this->ldap_result = @$function($this->conn, $this->base_dn, $filter,
- $attrs, 0, (int)$this->prop['sizelimit'], (int)$this->prop['timelimit'])
- ) {
- // when running on a patched PHP we can use the extended functions to retrieve the total count from the LDAP search result
- if ($this->vlv_active && function_exists('ldap_parse_virtuallist_control')) {
- if (ldap_parse_result($this->conn, $this->ldap_result,
- $errcode, $matcheddn, $errmsg, $referrals, $serverctrls)
- && $serverctrls // can be null e.g. in case of adm. limit error
- ) {
- ldap_parse_virtuallist_control($this->conn, $serverctrls,
- $last_offset, $this->vlv_count, $vresult);
- $this->_debug("S: VLV result: last_offset=$last_offset; content_count=$this->vlv_count");
- }
- else {
- $this->_debug("S: ".($errmsg ? $errmsg : ldap_error($this->conn)));
- }
+ $filter = $this->filter ? $this->filter : '(objectclass=*)';
+ $attrs = $count ? array('dn') : array_values($this->fieldmap); // only fetch dn for count (should keep the payload low)
+ if ($this->ldap_result = $this->ldap_search($base_dn, $filter, $prop['scope'], $attrs, $prop)) {
+ if ($count) {
+ return ldap_count_entries($this->conn, $this->ldap_result);
+ }
+ else {
+ return true;
}
-
- $entries_count = ldap_count_entries($this->conn, $this->ldap_result);
- $this->_debug("S: $entries_count record(s)");
-
- return $count ? $entries_count : true;
- }
- else {
- $this->_debug("S: ".ldap_error($this->conn));
}
}
@@ -1540,20 +1503,19 @@ class rcube_ldap extends rcube_addressbook
private function _ldap2result($rec)
{
$out = array('_type' => 'person');
+ $fieldmap = $this->fieldmap;
if ($rec['dn'])
$out[$this->primary_key] = self::dn_encode($rec['dn']);
// determine record type
- if (array_intersect(array('groupofuniquenames','kolabgroupofuniquenames'), (array)$rec['objectclass'])) {
+ if (array_intersect(array('groupofuniquenames','kolabgroupofuniquenames'), array_map('strtolower', (array)$rec['objectclass']))) {
$out['_type'] = 'group';
$out['readonly'] = true;
- if ($this->fieldmap['groupname']) {
- $this->fieldmap['name'] = $this->fieldmap['groupname'];
- }
+ $fieldmap['name'] = $fieldmap['_groupname'];
}
- foreach ($this->fieldmap as $rf => $lf)
+ foreach ($fieldmap as $rf => $lf)
{
for ($i=0; $i < $rec[$lf]['count']; $i++) {
if (!($value = $rec[$lf][$i]))
@@ -1718,20 +1680,14 @@ class rcube_ldap extends rcube_addressbook
/**
* Setter for the current group
- * (empty, has to be re-implemented by extending class)
*/
function set_group($group_id)
{
- if ($group_id)
- {
- if (($group_cache = $this->cache->get('groups')) === null)
- $group_cache = $this->_fetch_groups();
-
+ if ($group_id) {
$this->group_id = $group_id;
- $this->group_data = $group_cache[$group_id];
+ $this->group_data = $this->get_group_entry($group_id);
}
- else
- {
+ else {
$this->group_id = 0;
$this->group_data = null;
}
@@ -1772,6 +1728,23 @@ class rcube_ldap extends rcube_addressbook
*/
private function _fetch_groups($vlv_page = 0)
{
+ // special case: list groups from 'group_filters' config
+ if (!empty($this->prop['group_filters'])) {
+ $groups = array();
+
+ // list regular groups configuration as special filter
+ if (!empty($this->prop['groups']['filter'])) {
+ $id = '__groups__';
+ $groups[$id] = array('ID' => $id, 'name' => rcube_label('groups')) + $this->prop['groups'];
+ }
+
+ foreach ($this->prop['group_filters'] as $id => $prop) {
+ $groups[$id] = $prop + array('ID' => $id, 'name' => ucfirst($id));
+ }
+
+ return $groups;
+ }
+
$base_dn = $this->groups_base_dn;
$filter = $this->prop['groups']['filter'];
$name_attr = $this->prop['groups']['name_attr'];
@@ -1844,6 +1817,40 @@ class rcube_ldap extends rcube_addressbook
}
/**
+ * Fetch a group entry from LDAP and save in local cache
+ */
+ private function get_group_entry($group_id)
+ {
+ if (($group_cache = $this->cache->get('groups')) === null)
+ $group_cache = $this->_fetch_groups();
+
+ // add group record to cache if it isn't yet there
+ if (!isset($group_cache[$group_id])) {
+ $name_attr = $this->prop['groups']['name_attr'];
+ $dn = self::dn_decode($group_id);
+
+ $this->_debug("C: Read Group [dn: $dn]");
+ if ($result = @ldap_read($this->conn, $dn, '(objectClass=*)', array('dn','objectClass','member','uniqueMember','memberURL',$name_attr))) {
+ $list = ldap_get_entries($this->conn, $result);
+ $entry = $list[0];
+ $group_name = is_array($entry[$name_attr]) ? $entry[$name_attr][0] : $entry[$name_attr];
+ $group_cache[$group_id]['ID'] = $group_id;
+ $group_cache[$group_id]['dn'] = $dn;
+ $group_cache[$group_id]['name'] = $group_name;
+ $group_cache[$group_id]['member_attr'] = $this->get_group_member_attr($entry['objectclass']);
+ }
+ else {
+ $this->_debug("S: ".ldap_error($this->conn));
+ $group_cache[$group_id] = false;
+ }
+
+ $this->cache->set('groups', $group_cache);
+ }
+
+ return $group_cache[$group_id];
+ }
+
+ /**
* Get group properties such as name and email address(es)
*
* @param string Group identifier
@@ -2246,6 +2253,59 @@ class rcube_ldap extends rcube_addressbook
}
/**
+ * Wrapper for ldap_search or ldap_list depending on the given scope.
+ * It optionally uses VLV index if configured in $prop
+ *
+ * @param string Base DN to read from
+ * @param string Query filter to use
+ * @param string Listing scope (sub|list|base)
+ * @param array List of entry attributes to read
+ * @param array Hash array with query configuration properties:
+ * - vlv: true if VLV index should be used
+ * - sort: array of sort attributes (has to be in sync with the VLV index)
+ */
+ protected function ldap_search($base_dn, $filter = '(objectclass=*)', $scope = 'sub', $attrs = array('dn'), $prop = array())
+ {
+ if ($this->ready) {
+ $this->_debug("C: LDAP Search [$filter][dn: $base_dn]");
+
+ // set VLV controls if requested
+ if ($prop['vlv'] && $scope != 'base')
+ $this->vlv_active = $this->_vlv_set_controls($prop, $this->list_page, $this->page_size);
+
+ $function = $this->_scope2func($scope);
+ if ($ldap_result = $function($this->conn, $base_dn, $filter,
+ $attrs, 0, (int)$this->prop['sizelimit'], (int)$this->prop['timelimit'])
+ ) {
+ // when running on a patched PHP we can use the extended functions to retrieve the total count from the LDAP search result
+ if ($this->vlv_active && function_exists('ldap_parse_virtuallist_control')) {
+ if (ldap_parse_result($this->conn, $this->ldap_result,
+ $errcode, $matcheddn, $errmsg, $referrals, $serverctrls)
+ && $serverctrls // can be null e.g. in case of adm. limit error
+ ) {
+ ldap_parse_virtuallist_control($this->conn, $serverctrls,
+ $last_offset, $this->vlv_count, $vresult);
+ $this->_debug("S: VLV result: last_offset=$last_offset; content_count=$this->vlv_count");
+ }
+ else {
+ $this->_debug("S: ".($errmsg ? $errmsg : ldap_error($this->conn)));
+ }
+ }
+
+ $entries_count = ldap_count_entries($this->conn, $ldap_result);
+ $this->_debug("S: $entries_count record(s)");
+
+ return $ldap_result;
+ }
+ else {
+ $this->_debug("S: ".ldap_error($this->conn));
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Wrapper for ldap_add()
*/
protected function ldap_add($dn, $entry)