| | |
| | | { |
| | | /** public properties */ |
| | | public $primary_key = 'ID'; |
| | | public $groups = false; |
| | | public $readonly = true; |
| | | public $ready = false; |
| | | public $group_id = 0; |
| | | public $coltypes = array(); |
| | | public $groups = false; |
| | | public $readonly = true; |
| | | public $ready = false; |
| | | public $group_id = 0; |
| | | public $coltypes = array(); |
| | | |
| | | /** private properties */ |
| | | protected $ldap; |
| | | protected $prop = array(); |
| | | protected $prop = array(); |
| | | protected $fieldmap = array(); |
| | | protected $filter = ''; |
| | | protected $sub_filter; |
| | | protected $filter = ''; |
| | | protected $result = null; |
| | | protected $ldap_result = null; |
| | | protected $result; |
| | | protected $ldap_result; |
| | | protected $mail_domain = ''; |
| | | protected $debug = false; |
| | | |
| | | private $base_dn = ''; |
| | | private $base_dn = ''; |
| | | private $groups_base_dn = ''; |
| | | private $group_url = null; |
| | | private $group_url; |
| | | private $cache; |
| | | |
| | | |
| | |
| | | $this->prop = $p; |
| | | |
| | | $fetch_attributes = array('objectClass'); |
| | | |
| | | if (isset($p['searchonly'])) |
| | | $this->searchonly = $p['searchonly']; |
| | | |
| | | // check if groups are configured |
| | | if (is_array($p['groups']) && count($p['groups'])) { |
| | |
| | | if (is_array($p['group_filters']) && count($p['group_filters'])) { |
| | | $this->groups = true; |
| | | |
| | | foreach ($p['group_filters'] as $group_filter) { |
| | | foreach ($p['group_filters'] as $k => $group_filter) { |
| | | // set default name attribute to cn |
| | | if (empty($group_filter['name_attr']) && empty($this->prop['groups']['name_attr'])) |
| | | $this->prop['group_filters'][$k]['name_attr'] = $group_filter['name_attr'] = 'cn'; |
| | | |
| | | if ($group_filter['name_attr']) |
| | | $fetch_attributes[] = $group_filter['name_attr']; |
| | | } |
| | |
| | | |
| | | // fetch group object |
| | | if (empty($entries)) { |
| | | $entries = $this->ldap->read_entries($dn, '(objectClass=*)', array('dn','objectClass','member','uniqueMember','memberURL')); |
| | | $attribs = array('dn','objectClass','member','uniqueMember','memberURL'); |
| | | $entries = $this->ldap->read_entries($dn, '(objectClass=*)', $attribs); |
| | | if ($entries === false) { |
| | | return $group_members; |
| | | } |
| | |
| | | |
| | | for ($i=0; $i < $entries['count']; $i++) { |
| | | $entry = $entries[$i]; |
| | | |
| | | if (empty($entry['objectclass'])) |
| | | continue; |
| | | $attrs = array(); |
| | | |
| | | foreach ((array)$entry['objectclass'] as $objectclass) { |
| | | switch (strtolower($objectclass)) { |
| | | case "group": |
| | | case "groupofnames": |
| | | case "kolabgroupofnames": |
| | | $group_members = array_merge($group_members, $this->_list_group_members($dn, $entry, 'member', $count)); |
| | | break; |
| | | case "groupofuniquenames": |
| | | case "kolabgroupofuniquenames": |
| | | $group_members = array_merge($group_members, $this->_list_group_members($dn, $entry, 'uniquemember', $count)); |
| | | break; |
| | | case "groupofurls": |
| | | $group_members = array_merge($group_members, $this->_list_group_memberurl($dn, $entry, $count)); |
| | | break; |
| | | if (strtolower($objectclass) == 'groupofurls') { |
| | | $members = $this->_list_group_memberurl($dn, $entry, $count); |
| | | $group_members = array_merge($group_members, $members); |
| | | } |
| | | else if (($member_attr = $this->get_group_member_attr(array($objectclass), '')) |
| | | && ($member_attr = strtolower($member_attr)) && !in_array($member_attr, $attrs) |
| | | ) { |
| | | $members = $this->_list_group_members($dn, $entry, $member_attr, $count); |
| | | $group_members = array_merge($group_members, $members); |
| | | $attrs[] = $member_attr; |
| | | } |
| | | |
| | | if ($this->prop['sizelimit'] && count($group_members) > $this->prop['sizelimit']) { |
| | | break 2; |
| | | } |
| | | } |
| | | |
| | | if ($this->prop['sizelimit'] && count($group_members) > $this->prop['sizelimit']) |
| | | break; |
| | | } |
| | | |
| | | return array_filter($group_members); |
| | |
| | | // Use the member attributes to return an array of member ldap objects |
| | | // NOTE that the member attribute is supposed to contain a DN |
| | | $group_members = array(); |
| | | if (empty($entry[$attr])) |
| | | if (empty($entry[$attr])) { |
| | | return $group_members; |
| | | } |
| | | |
| | | // read these attributes for all members |
| | | $attrib = $count ? array('dn','objectClass') : $this->prop['list_attributes']; |
| | |
| | | // change the group membership of the contact |
| | | if ($this->groups) { |
| | | $group_ids = $this->get_record_groups($dn); |
| | | foreach ($group_ids as $group_id => $group_prop) |
| | | { |
| | | foreach (array_keys($group_ids) as $group_id) { |
| | | $this->remove_from_group($group_id, $dn); |
| | | $this->add_to_group($group_id, $newdn); |
| | | } |
| | |
| | | if ($this->groups) { |
| | | $dn = self::dn_encode($dn); |
| | | $group_ids = $this->get_record_groups($dn); |
| | | foreach ($group_ids as $group_id => $group_prop) { |
| | | foreach (array_keys($group_ids) as $group_id) { |
| | | $this->remove_from_group($group_id, $dn); |
| | | } |
| | | } |
| | |
| | | private static function is_group_entry($entry) |
| | | { |
| | | return array_intersect( |
| | | array('group', 'groupofnames', 'kolabgroupofnames', 'groupofuniquenames','kolabgroupofuniquenames','groupofurls'), |
| | | array('group', 'groupofnames', 'kolabgroupofnames', 'groupofuniquenames', |
| | | 'kolabgroupofuniquenames', 'groupofurls', 'univentiongroup'), |
| | | array_map('strtolower', (array)$entry['objectclass']) |
| | | ); |
| | | } |
| | |
| | | */ |
| | | function list_groups($search = null, $mode = 0) |
| | | { |
| | | if (!$this->groups) |
| | | if (!$this->groups) { |
| | | return array(); |
| | | |
| | | // use cached list for searching |
| | | if (!$this->cache || ($group_cache = $this->cache->get('groups')) === null) { |
| | | $group_cache = $this->_fetch_groups(); |
| | | } |
| | | |
| | | $groups = array(); |
| | | $group_cache = $this->_fetch_groups(); |
| | | $groups = array(); |
| | | |
| | | if ($search) { |
| | | foreach ($group_cache as $group) { |
| | | if ($this->compare_search_value('name', $group['name'], $search, $mode)) { |
| | |
| | | } |
| | | } |
| | | } |
| | | else |
| | | else { |
| | | $groups = $group_cache; |
| | | } |
| | | |
| | | return array_values($groups); |
| | | } |
| | |
| | | /** |
| | | * Fetch groups from server |
| | | */ |
| | | private function _fetch_groups($vlv_page = 0) |
| | | private function _fetch_groups($vlv_page = null) |
| | | { |
| | | // special case: list groups from 'group_filters' config |
| | | if (!empty($this->prop['group_filters'])) { |
| | | if ($vlv_page === null && !empty($this->prop['group_filters'])) { |
| | | $groups = array(); |
| | | |
| | | // list regular groups configuration as special filter |
| | |
| | | return $groups; |
| | | } |
| | | |
| | | $base_dn = $this->groups_base_dn; |
| | | $filter = $this->prop['groups']['filter']; |
| | | $name_attr = $this->prop['groups']['name_attr']; |
| | | if ($this->cache && $vlv_page === null && ($groups = $this->cache->get('groups')) !== null) { |
| | | return $groups; |
| | | } |
| | | |
| | | $base_dn = $this->groups_base_dn; |
| | | $filter = $this->prop['groups']['filter']; |
| | | $name_attr = $this->prop['groups']['name_attr']; |
| | | $email_attr = $this->prop['groups']['email_attr'] ? $this->prop['groups']['email_attr'] : 'mail'; |
| | | $sort_attrs = $this->prop['groups']['sort'] ? (array)$this->prop['groups']['sort'] : array($name_attr); |
| | | $sort_attr = $sort_attrs[0]; |
| | | $sort_attr = $sort_attrs[0]; |
| | | |
| | | $ldap = $this->ldap; |
| | | |
| | | // use vlv to list groups |
| | | if ($this->prop['groups']['vlv']) { |
| | | $page_size = 200; |
| | | if (!$this->prop['groups']['sort']) |
| | | if (!$this->prop['groups']['sort']) { |
| | | $this->prop['groups']['sort'] = $sort_attrs; |
| | | } |
| | | |
| | | $ldap = clone $this->ldap; |
| | | $ldap->set_config($this->prop['groups']); |
| | | $ldap->set_vlv_page($vlv_page+1, $page_size); |
| | | } |
| | | |
| | | $attrs = array_unique(array('dn', 'objectClass', $name_attr, $email_attr, $sort_attr)); |
| | | $attrs = array_unique(array('dn', 'objectClass', $name_attr, $email_attr, $sort_attr)); |
| | | $ldap_data = $ldap->search($base_dn, $filter, $this->prop['groups']['scope'], $attrs, $this->prop['groups']); |
| | | |
| | | if ($ldap_data === false) { |
| | | return array(); |
| | | } |
| | | |
| | | $groups = array(); |
| | | $groups = array(); |
| | | $group_sortnames = array(); |
| | | $group_count = $ldap_data->count(); |
| | | $group_count = $ldap_data->count(); |
| | | |
| | | foreach ($ldap_data as $entry) { |
| | | if (!$entry['dn']) // DN is mandatory |
| | | $entry['dn'] = $ldap_data->get_dn(); |
| | |
| | | } |
| | | |
| | | // recursive call can exit here |
| | | if ($vlv_page > 0) |
| | | if ($vlv_page > 0) { |
| | | return $groups; |
| | | } |
| | | |
| | | // call recursively until we have fetched all groups |
| | | while ($this->prop['groups']['vlv'] && $group_count == $page_size) { |
| | | $next_page = $this->_fetch_groups(++$vlv_page); |
| | | $groups = array_merge($groups, $next_page); |
| | | $next_page = $this->_fetch_groups(++$vlv_page); |
| | | $groups = array_merge($groups, $next_page); |
| | | $group_count = count($next_page); |
| | | } |
| | | |
| | | // when using VLV the list of groups is already sorted |
| | | if (!$this->prop['groups']['vlv']) |
| | | if (!$this->prop['groups']['vlv']) { |
| | | array_multisort($group_sortnames, SORT_ASC, SORT_STRING, $groups); |
| | | } |
| | | |
| | | // cache this |
| | | if ($this->cache) { |
| | |
| | | */ |
| | | private function get_group_entry($group_id) |
| | | { |
| | | if (!$this->cache || ($group_cache = $this->cache->get('groups')) === null) { |
| | | $group_cache = $this->_fetch_groups(); |
| | | } |
| | | $group_cache = $this->_fetch_groups(); |
| | | |
| | | // add group record to cache if it isn't yet there |
| | | if (!isset($group_cache[$group_id])) { |
| | |
| | | */ |
| | | function create_group($group_name) |
| | | { |
| | | $new_dn = 'cn=' . rcube_ldap_generic::quote_string($group_name, true) . ',' . $this->groups_base_dn; |
| | | $new_gid = self::dn_encode($new_dn); |
| | | $new_dn = 'cn=' . rcube_ldap_generic::quote_string($group_name, true) . ',' . $this->groups_base_dn; |
| | | $new_gid = self::dn_encode($new_dn); |
| | | $member_attr = $this->get_group_member_attr(); |
| | | $name_attr = $this->prop['groups']['name_attr'] ? $this->prop['groups']['name_attr'] : 'cn'; |
| | | |
| | | $new_entry = array( |
| | | $name_attr = $this->prop['groups']['name_attr'] ? $this->prop['groups']['name_attr'] : 'cn'; |
| | | $new_entry = array( |
| | | 'objectClass' => $this->prop['groups']['object_classes'], |
| | | $name_attr => $group_name, |
| | | $member_attr => '', |
| | |
| | | */ |
| | | function delete_group($group_id) |
| | | { |
| | | if (!$this->cache || ($group_cache = $this->cache->get('groups')) === null) { |
| | | $group_cache = $this->_fetch_groups(); |
| | | } |
| | | |
| | | $del_dn = $group_cache[$group_id]['dn']; |
| | | $group_cache = $this->_fetch_groups(); |
| | | $del_dn = $group_cache[$group_id]['dn']; |
| | | |
| | | if (!$this->ldap->delete($del_dn)) { |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | |
| | | */ |
| | | function rename_group($group_id, $new_name, &$new_gid) |
| | | { |
| | | if (!$this->cache || ($group_cache = $this->cache->get('groups')) === null) { |
| | | $group_cache = $this->_fetch_groups(); |
| | | } |
| | | |
| | | $old_dn = $group_cache[$group_id]['dn']; |
| | | $new_rdn = "cn=" . rcube_ldap_generic::quote_string($new_name, true); |
| | | $new_gid = self::dn_encode($new_rdn . ',' . $this->groups_base_dn); |
| | | $group_cache = $this->_fetch_groups(); |
| | | $old_dn = $group_cache[$group_id]['dn']; |
| | | $new_rdn = "cn=" . rcube_ldap_generic::quote_string($new_name, true); |
| | | $new_gid = self::dn_encode($new_rdn . ',' . $this->groups_base_dn); |
| | | |
| | | if (!$this->ldap->rename($old_dn, $new_rdn, null, true)) { |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | |
| | | */ |
| | | function add_to_group($group_id, $contact_ids) |
| | | { |
| | | if (!$this->cache || ($group_cache = $this->cache->get('groups')) === null) { |
| | | $group_cache = $this->_fetch_groups(); |
| | | } |
| | | |
| | | if (!is_array($contact_ids)) |
| | | $contact_ids = explode(',', $contact_ids); |
| | | |
| | | $group_cache = $this->_fetch_groups(); |
| | | $member_attr = $group_cache[$group_id]['member_attr']; |
| | | $group_dn = $group_cache[$group_id]['dn']; |
| | | $new_attrs = array(); |
| | | |
| | | foreach ($contact_ids as $id) |
| | | if (!is_array($contact_ids)) { |
| | | $contact_ids = explode(',', $contact_ids); |
| | | } |
| | | |
| | | foreach ($contact_ids as $id) { |
| | | $new_attrs[$member_attr][] = self::dn_decode($id); |
| | | } |
| | | |
| | | if (!$this->ldap->mod_add($group_dn, $new_attrs)) { |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | |
| | | */ |
| | | function remove_from_group($group_id, $contact_ids) |
| | | { |
| | | if (!$this->cache || ($group_cache = $this->cache->get('groups')) === null) { |
| | | $group_cache = $this->_fetch_groups(); |
| | | } |
| | | |
| | | if (!is_array($contact_ids)) |
| | | $contact_ids = explode(',', $contact_ids); |
| | | |
| | | $group_cache = $this->_fetch_groups(); |
| | | $member_attr = $group_cache[$group_id]['member_attr']; |
| | | $group_dn = $group_cache[$group_id]['dn']; |
| | | $del_attrs = array(); |
| | | $del_attrs = array(); |
| | | |
| | | foreach ($contact_ids as $id) |
| | | if (!is_array($contact_ids)) { |
| | | $contact_ids = explode(',', $contact_ids); |
| | | } |
| | | |
| | | foreach ($contact_ids as $id) { |
| | | $del_attrs[$member_attr][] = self::dn_decode($id); |
| | | } |
| | | |
| | | if (!$this->ldap->mod_del($group_dn, $del_attrs)) { |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | |
| | | */ |
| | | function get_record_groups($contact_id) |
| | | { |
| | | if (!$this->groups) |
| | | if (!$this->groups) { |
| | | return array(); |
| | | } |
| | | |
| | | $base_dn = $this->groups_base_dn; |
| | | $contact_dn = self::dn_decode($contact_id); |
| | |
| | | $entry['dn'] = $ldap_data->get_dn(); |
| | | $group_name = $entry[$name_attr][0]; |
| | | $group_id = self::dn_encode($entry['dn']); |
| | | $groups[$group_id] = array('ID' => $group_id, 'name' => $group_name, 'dn' => $entry['dn']); |
| | | $groups[$group_id] = $group_name; |
| | | } |
| | | |
| | | return $groups; |
| | | } |
| | | |
| | | /** |
| | | * Detects group member attribute name |
| | | */ |
| | | private function get_group_member_attr($object_classes = array()) |
| | | private function get_group_member_attr($object_classes = array(), $default = 'member') |
| | | { |
| | | if (empty($object_classes)) { |
| | | $object_classes = $this->prop['groups']['object_classes']; |
| | | } |
| | | |
| | | if (!empty($object_classes)) { |
| | | foreach ((array)$object_classes as $oc) { |
| | | switch (strtolower($oc)) { |
| | | case 'group': |
| | | case 'groupofnames': |
| | | case 'kolabgroupofnames': |
| | | $member_attr = 'member'; |
| | | break; |
| | | return 'member'; |
| | | |
| | | case 'groupofuniquenames': |
| | | case 'kolabgroupofuniquenames': |
| | | $member_attr = 'uniqueMember'; |
| | | break; |
| | | case 'univentiongroup': |
| | | return 'uniqueMember'; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!empty($member_attr)) { |
| | | return $member_attr; |
| | | } |
| | | |
| | | if (!empty($this->prop['groups']['member_attr'])) { |
| | | return $this->prop['groups']['member_attr']; |
| | | } |
| | | |
| | | return 'member'; |
| | | return $default; |
| | | } |
| | | |
| | | |