Searching in both contacts and groups when LDAP addressbook with group_filters option is used

Conflicts:

	CHANGELOG
	program/steps/addressbook/search.inc
pull/5754/head
Aleksander Machniak 8 years ago
parent 86b329b1fa
commit 6ccfcab17a

@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
- Searching in both contacts and groups when LDAP addressbook with group_filters option is used
- Fix vulnerability in handling of mail()'s 5th argument
- Fix To: header encoding in mail sent with mail() method (#5475)
- Fix flickering of header topline in min-mode (#5426)

@ -1266,21 +1266,37 @@ function rcube_webmail()
break;
case 'pushgroup':
// add group ID to stack
this.env.address_group_stack.push(props.id);
// add group ID and current search to stack
var group = {
id: props.id,
search_request: this.env.search_request,
page: this.env.current_page,
search: this.env.search_request && this.gui_objects.qsearchbox ? this.gui_objects.qsearchbox.value : null
};
this.env.address_group_stack.push(group);
if (obj && event)
rcube_event.cancel(event);
case 'listgroup':
this.reset_qsearch();
this.list_contacts(props.source, props.id);
this.list_contacts(props.source, props.id, 1, group);
break;
case 'popgroup':
if (this.env.address_group_stack.length > 1) {
this.env.address_group_stack.pop();
if (this.env.address_group_stack.length) {
var old = this.env.address_group_stack.pop();
this.reset_qsearch();
this.list_contacts(props.source, this.env.address_group_stack[this.env.address_group_stack.length-1]);
if (old.search_request) {
// this code is executed when going back to the search result
if (old.search && this.gui_objects.qsearchbox)
$(this.gui_objects.qsearchbox).val(old.search);
this.env.search_request = old.search_request;
this.list_contacts_remote(null, null, this.env.current_page = old.page);
}
else
this.list_contacts(props.source, this.env.address_group_stack[this.env.address_group_stack.length-1].id);
}
break;
@ -5453,9 +5469,9 @@ function rcube_webmail()
return false;
};
this.list_contacts = function(src, group, page)
this.list_contacts = function(src, group, page, search)
{
var win, folder, url = {},
var win, folder, index = -1, url = {},
refresh = src === undefined && group === undefined && page === undefined,
target = window;
@ -5465,9 +5481,6 @@ function rcube_webmail()
if (refresh)
group = this.env.group;
if (page && this.current_page == page && src == this.env.source && group == this.env.group)
return false;
if (src != this.env.source) {
page = this.env.current_page = 1;
this.reset_qsearch();
@ -5484,21 +5497,26 @@ function rcube_webmail()
this.env.group = group;
// truncate groups listing stack
var index = $.inArray(this.env.group, this.env.address_group_stack);
if (index < 0)
this.env.address_group_stack = [];
else
this.env.address_group_stack = this.env.address_group_stack.slice(0,index);
$.each(this.env.address_group_stack, function(i, v) {
if (ref.env.group == v.id) {
index = i;
return false;
}
});
this.env.address_group_stack = index < 0 ? [] : this.env.address_group_stack.slice(0, index);
// make sure the current group is on top of the stack
if (this.env.group) {
this.env.address_group_stack.push(this.env.group);
if (!search) search = {};
search.id = this.env.group;
this.env.address_group_stack.push(search);
// mark the first group on the stack as selected in the directory list
folder = 'G'+src+this.env.address_group_stack[0];
folder = 'G'+src+this.env.address_group_stack[0].id;
}
else if (this.gui_objects.addresslist_title) {
$(this.gui_objects.addresslist_title).html(this.get_label('contacts'));
$(this.gui_objects.addresslist_title).text(this.get_label('contacts'));
}
if (!this.env.search_id)
@ -5571,7 +5589,9 @@ function rcube_webmail()
var boxtitle = $(this.gui_objects.addresslist_title).html(''); // clear contents
// add link to pop back to parent group
if (this.env.address_group_stack.length > 1) {
if (this.env.address_group_stack.length > 1
|| (this.env.address_group_stack.length == 1 && this.env.address_group_stack[0].search_request)
) {
$('<a href="#list">...</a>')
.attr('title', this.get_label('uponelevel'))
.addClass('poplink')
@ -5580,10 +5600,11 @@ function rcube_webmail()
boxtitle.append('&nbsp;&raquo;&nbsp;');
}
boxtitle.append($('<span>').text(prop.name));
boxtitle.append($('<span>').text(prop ? prop.name : this.get_label('contacts')));
}
this.triggerEvent('groupupdate', prop);
if (prop)
this.triggerEvent('groupupdate', prop);
};
// load contact record

@ -567,30 +567,15 @@ class rcube_ldap extends rcube_addressbook
$this->result = new rcube_result_set($entries['count'], ($this->list_page-1) * $this->page_size);
}
else {
$prop = $this->group_id ? $this->group_data : $this->prop;
$base_dn = $this->group_id ? $prop['base_dn'] : $this->base_dn;
// use global search filter
if (!empty($this->filter))
$prop['filter'] = $this->filter;
// exec LDAP search if no result resource is stored
if ($this->ready && !$this->ldap_result)
$this->ldap_result = $this->ldap->search($base_dn, $prop['filter'], $prop['scope'], $this->prop['attributes'], $prop);
if ($this->ready && $this->ldap_result === null) {
$this->ldap_result = $this->extended_search();
}
// count contacts for this user
$this->result = $this->count();
// we have a search result resource
if ($this->ldap_result && $this->result->count > 0) {
// sorting still on the ldap server
if ($this->sort_col && $prop['scope'] !== 'base' && !$this->ldap->vlv_active)
$this->ldap_result->sort($this->sort_col);
// get all entries from the ldap server
$entries = $this->ldap_result->entries();
}
$entries = $this->ldap_result;
} // end else
// start and end of the page
@ -750,7 +735,7 @@ class rcube_ldap extends rcube_addressbook
* @param boolean $nocount (Not used)
* @param array $required List of fields that cannot be empty
*
* @return array Indexed list of contact records and 'count' value
* @return rcube_result_set List of contact records
*/
function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array())
{
@ -881,7 +866,9 @@ class rcube_ldap extends rcube_addressbook
}
// set filter string and execute search
$this->set_search_set($filter);
// @FIXME: we need a better way to detect/define when groups are allowed in the result
$prefix = empty($required) ? 'e:' : '';
$this->set_search_set($prefix . $filter);
if ($select)
$this->list_records();
@ -899,24 +886,97 @@ class rcube_ldap extends rcube_addressbook
function count()
{
$count = 0;
if ($this->ldap_result) {
$count = $this->ldap_result->count();
if (!empty($this->ldap_result)) {
$count = $this->ldap_result['count'];
}
else if ($this->group_id && $this->group_data['dn']) {
$count = count($this->list_group_members($this->group_data['dn'], true));
}
// We have a connection but no result set, attempt to get one.
else if ($this->ready) {
$prop = $this->group_id ? $this->group_data : $this->prop;
$base_dn = $this->group_id ? $this->group_base_dn : $this->base_dn;
$count = $this->extended_search(true);
}
return new rcube_result_set($count, ($this->list_page-1) * $this->page_size);
}
/**
* Wrapper on LDAP searches with group_filters support, which
* allows searching for contacts AND groups.
*
* @param bool $count Return count instead of the records
*
* @return int|array Count of records or the result array (with 'count' item)
*/
protected function extended_search($count = false)
{
$prop = $this->group_id ? $this->group_data : $this->prop;
$base_dn = $this->group_id ? $this->groups_base_dn : $this->base_dn;
$attrs = $count ? array('dn') : $this->prop['attributes'];
$entries = array();
// Use global search filter
if ($filter = $this->filter) {
if ($filter[0] == 'e' && $filter[1] == ':') {
$filter = substr($filter, 2);
$is_extended_search = !$this->group_id;
}
if (!empty($this->filter)) { // Use global search filter
$prop['filter'] = $this->filter;
$prop['filter'] = $filter;
// add general filter to query
if (!empty($this->prop['filter'])) {
$prop['filter'] = '(&(' . preg_replace('/^\(|\)$/', '', $this->prop['filter']) . ')' . $prop['filter'] . ')';
}
$count = $this->ldap->search($base_dn, $prop['filter'], $prop['scope'], array('dn'), $prop, true);
}
return new rcube_result_set($count, ($this->list_page-1) * $this->page_size);
$result = $this->ldap->search($base_dn, $prop['filter'], $prop['scope'], $attrs, $prop, $count);
// we have a search result resource, get all entries
if (!$count && $result && $result->count() > 0) {
$result = $result->entries();
unset($result['count']);
}
// search for groups
if ($is_extended_search
&& is_array($this->prop['group_filters'])
&& !empty($this->prop['groups']['filter'])
) {
$filter = '(&(' . preg_replace('/^\(|\)$/', '', $this->prop['groups']['filter']) . ')' . $filter . ')';
// for groups we may use cn instead of displayname...
if ($this->prop['fieldmap']['name'] != $this->prop['groups']['name_attr']) {
$filter = str_replace(strtolower($this->prop['fieldmap']['name']) . '=', $this->prop['groups']['name_attr'] . '=', $filter);
}
$name_attr = $this->prop['groups']['name_attr'];
$email_attr = $this->prop['groups']['email_attr'] ?: 'mail';
$attrs = array_unique(array('dn', 'objectClass', $name_attr, $email_attr));
$res = $this->ldap->search($this->groups_base_dn, $filter, $this->prop['groups']['scope'], $attrs, $prop, $count);
if ($count && $res) {
$result += $res;
}
else if (!$count && $res && $res->count()) {
$res = $res->entries();
unset($res['count']);
$result = array_merge($result, $res);
}
}
if (!$count && $result) {
// sorting
if ($this->sort_col && $prop['scope'] !== 'base' && !$this->ldap->vlv_active) {
usort($result, array($this, '_entry_sort_cmp'));
}
$result['count'] = count($result);
$this->result_entries = $result;
}
return $result;
}
/**

@ -57,11 +57,13 @@ else {
}
if ($CONTACTS->group_id) {
$OUTPUT->command('set_group_prop', array('ID' => $CONTACTS->group_id)
+ array_intersect_key((array)$CONTACTS->get_group($CONTACTS->group_id), array('name'=>1,'email'=>1)));
$group_data = array('ID' => $CONTACTS->group_id)
+ array_intersect_key((array)$CONTACTS->get_group($CONTACTS->group_id), array('name'=>1,'email'=>1));
}
}
$OUTPUT->command('set_group_prop', $group_data);
// update message count display
$OUTPUT->set_env('pagecount', ceil($result->count / $PAGE_SIZE));
$OUTPUT->command('set_rowcount', rcmail_get_rowcount_text($result));

@ -230,13 +230,15 @@ function rcmail_contact_search()
}
// update message count display
$OUTPUT->command('set_env', 'search_request', $search_request);
$OUTPUT->command('set_env', 'pagecount', ceil($result->count / $PAGE_SIZE));
$OUTPUT->set_env('search_request', $search_request);
$OUTPUT->set_env('pagecount', ceil($result->count / $PAGE_SIZE));
$OUTPUT->command('set_rowcount', rcmail_get_rowcount_text($result));
// Re-set current source
$OUTPUT->command('set_env', 'search_id', $sid);
$OUTPUT->command('set_env', 'source', '');
$OUTPUT->command('set_env', 'group', '');
$OUTPUT->set_env('search_id', $sid);
$OUTPUT->set_env('source', '');
$OUTPUT->set_env('group', '');
// Re-set list header
$OUTPUT->command('set_group_prop', null);
if (!$sid) {
// unselect currently selected directory/group

Loading…
Cancel
Save