| | |
| | | +-----------------------------------------------------------------------+ |
| | | | program/include/rcube_ldap.php | |
| | | | | |
| | | | This file is part of the RoundCube Webmail client | |
| | | | Copyright (C) 2006-2009, RoundCube Dev. - Switzerland | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2006-2011, The Roundcube Dev Team | |
| | | | Licensed under the GNU GPL | |
| | | | | |
| | | | PURPOSE: | |
| | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | | | Andreas Dick <andudi (at) gmx (dot) ch> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | $Id$ |
| | |
| | | */ |
| | | class rcube_ldap extends rcube_addressbook |
| | | { |
| | | var $conn; |
| | | var $prop = array(); |
| | | var $fieldmap = array(); |
| | | |
| | | var $filter = ''; |
| | | var $result = null; |
| | | var $ldap_result = null; |
| | | var $sort_col = ''; |
| | | var $mail_domain = ''; |
| | | var $debug = false; |
| | | |
| | | protected $conn; |
| | | protected $prop = array(); |
| | | protected $fieldmap = array(); |
| | | |
| | | protected $filter = ''; |
| | | protected $result = null; |
| | | protected $ldap_result = null; |
| | | protected $sort_col = ''; |
| | | protected $mail_domain = ''; |
| | | protected $debug = false; |
| | | |
| | | /** public properties */ |
| | | var $primary_key = 'ID'; |
| | | var $readonly = true; |
| | | var $list_page = 1; |
| | | var $page_size = 10; |
| | | var $ready = false; |
| | | |
| | | |
| | | public $primary_key = 'ID'; |
| | | public $readonly = true; |
| | | public $groups = false; |
| | | public $list_page = 1; |
| | | public $page_size = 10; |
| | | public $group_id = 0; |
| | | public $ready = false; |
| | | public $coltypes = array(); |
| | | |
| | | private $group_cache = array(); |
| | | private $group_members = array(); |
| | | |
| | | |
| | | /** |
| | | * Object constructor |
| | | * |
| | |
| | | { |
| | | $this->prop = $p; |
| | | |
| | | foreach ($p as $prop => $value) |
| | | if (preg_match('/^(.+)_field$/', $prop, $matches)) |
| | | $this->fieldmap[$matches[1]] = $this->_attr_name(strtolower($value)); |
| | | // check if groups are configured |
| | | if (is_array($p['groups'])) |
| | | $this->groups = true; |
| | | |
| | | // fieldmap property is given |
| | | if (is_array($p['fieldmap'])) { |
| | | foreach ($p['fieldmap'] as $rf => $lf) |
| | | $this->fieldmap[$rf] = $this->_attr_name(strtolower($lf)); |
| | | } |
| | | else { |
| | | // read deprecated *_field properties to remain backwards compatible |
| | | foreach ($p as $prop => $value) |
| | | if (preg_match('/^(.+)_field$/', $prop, $matches)) |
| | | $this->fieldmap[$matches[1]] = $this->_attr_name(strtolower($value)); |
| | | } |
| | | |
| | | // use fieldmap to advertise supported coltypes to the application |
| | | foreach ($this->fieldmap as $col => $lf) { |
| | | list($col, $type) = explode(':', $col); |
| | | if (!is_array($this->coltypes[$col])) { |
| | | $subtypes = $type ? array($type) : null; |
| | | $this->coltypes[$col] = array('limit' => 2, 'subtypes' => $subtypes); |
| | | } |
| | | else if ($type) { |
| | | $this->coltypes[$col]['subtypes'][] = $type; |
| | | $this->coltypes[$col]['limit']++; |
| | | } |
| | | if ($type && !$this->fieldmap[$col]) |
| | | $this->fieldmap[$col] = $lf; |
| | | } |
| | | |
| | | if ($this->fieldmap['street'] && $this->fieldmap['locality']) |
| | | $this->coltypes['address'] = array('limit' => 1); |
| | | else if ($this->coltypes['address']) |
| | | $this->coltypes['address'] = array('type' => 'textarea', 'childs' => null, 'limit' => 1, 'size' => 40); |
| | | |
| | | // make sure 'required_fields' is an array |
| | | if (!is_array($this->prop['required_fields'])) |
| | |
| | | global $RCMAIL; |
| | | |
| | | if (!function_exists('ldap_connect')) |
| | | raise_error(array('code' => 100, 'type' => 'ldap', 'message' => "No ldap support in this installation of PHP"), true); |
| | | raise_error(array('code' => 100, 'type' => 'ldap', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "No ldap support in this installation of PHP"), true); |
| | | |
| | | if (is_resource($this->conn)) |
| | | return true; |
| | | |
| | | |
| | | if (!is_array($this->prop['hosts'])) |
| | | $this->prop['hosts'] = array($this->prop['hosts']); |
| | | |
| | |
| | | |
| | | foreach ($this->prop['hosts'] as $host) |
| | | { |
| | | $host = rcube_idn_to_ascii(rcube_parse_host($host)); |
| | | $this->_debug("C: Connect [$host".($this->prop['port'] ? ':'.$this->prop['port'] : '')."]"); |
| | | |
| | | if ($lc = @ldap_connect($host, $this->prop['port'])) |
| | |
| | | // Get the pieces needed for variable replacement. |
| | | $fu = $RCMAIL->user->get_username(); |
| | | list($u, $d) = explode('@', $fu); |
| | | |
| | | $dc = 'dc='.strtr($d, array('.' => ',dc=')); // hierarchal domain string |
| | | |
| | | $replaces = array('%dc' => $dc, '%d' => $d, '%fu' => $fu, '%u' => $u); |
| | | |
| | | if ($this->prop['search_base_dn'] && $this->prop['search_filter']) { |
| | | // 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); |
| | | |
| | | $this->_debug("S: searching with base {$this->prop['search_base_dn']} for {$this->prop['search_filter']}"); |
| | | |
| | | $res = ldap_search($this->conn, $this->prop['search_base_dn'], $this->prop['search_filter'], array('uid')); |
| | | if ($res && ($entry = ldap_first_entry($this->conn, $res))) { |
| | | $bind_dn = ldap_get_dn($this->conn, $entry); |
| | | |
| | | $this->_debug("S: search returned dn: $bind_dn"); |
| | | |
| | | if ($bind_dn) { |
| | | $this->prop['bind_dn'] = $bind_dn; |
| | | $dn = ldap_explode_dn($bind_dn, 1); |
| | | $replaces['%dn'] = $dn[0]; |
| | | } |
| | | } |
| | | } |
| | | // Replace the bind_dn and base_dn variables. |
| | | $replaces = array('%fu' => $fu, '%u' => $u, '%d' => $d); |
| | | $this->prop['bind_dn'] = strtr($this->prop['bind_dn'], $replaces); |
| | | $this->prop['base_dn'] = strtr($this->prop['base_dn'], $replaces); |
| | | } |
| | | |
| | | |
| | | if (!empty($this->prop['bind_dn']) && !empty($this->prop['bind_pass'])) |
| | | $this->ready = $this->bind($this->prop['bind_dn'], $this->prop['bind_pass']); |
| | | } |
| | | else |
| | | raise_error(array('code' => 100, 'type' => 'ldap', 'message' => "Could not connect to any LDAP server, tried $host:{$this->prop[port]} last"), true); |
| | | raise_error(array('code' => 100, 'type' => 'ldap', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Could not connect to any LDAP server, last tried $host:{$this->prop[port]}"), true); |
| | | |
| | | // See if the directory is writeable. |
| | | if ($this->prop['writable']) { |
| | |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | |
| | | raise_error(array( |
| | | 'code' => ldap_errno($this->conn), |
| | | 'type' => 'ldap', |
| | | 'code' => ldap_errno($this->conn), 'type' => 'ldap', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Bind failed for dn=$dn: ".ldap_error($this->conn)), |
| | | true); |
| | | |
| | |
| | | $this->result->add($this->_ldap2result($entries[$i])); |
| | | } |
| | | |
| | | // temp hack for filtering group members |
| | | if ($this->group_id) |
| | | { |
| | | $result = new rcube_result_set(); |
| | | while ($record = $this->result->iterate()) |
| | | { |
| | | if ($this->group_members[$record['ID']]) |
| | | { |
| | | $result->add($record); |
| | | $result->count++; |
| | | } |
| | | } |
| | | $this->result = $result; |
| | | } |
| | | |
| | | return $this->result; |
| | | } |
| | | |
| | |
| | | * |
| | | * @param array List of fields to search in |
| | | * @param string Search value |
| | | * @param boolean True for strict, False for partial (fuzzy) matching |
| | | * @param boolean True if results are requested, False if count only |
| | | * @param boolean (Not used) |
| | | * @param array List of fields that cannot be empty |
| | | * @return array Indexed list of contact records and 'count' value |
| | | */ |
| | | function search($fields, $value, $strict=false, $select=true) |
| | | function search($fields, $value, $strict=false, $select=true, $nocount=false, $required=array()) |
| | | { |
| | | // special treatment for ID-based search |
| | | if ($fields == 'ID' || $fields == $this->primary_key) |
| | |
| | | $filter .= "($f=$wc" . rcube_ldap::quote_string($value) . "$wc)"; |
| | | } |
| | | $filter .= ')'; |
| | | |
| | | |
| | | // add required (non empty) fields filter |
| | | $req_filter = ''; |
| | | foreach ((array)$required as $field) |
| | | if ($f = $this->_map_field($field)) |
| | | $req_filter .= "($f=*)"; |
| | | |
| | | if (!empty($req_filter)) |
| | | $filter = '(&' . $req_filter . $filter . ')'; |
| | | |
| | | // avoid double-wildcard if $value is empty |
| | | $filter = preg_replace('/\*+/', '*', $filter); |
| | | |
| | | |
| | | // add general filter to query |
| | | if (!empty($this->prop['filter'])) |
| | | $filter = '(&(' . preg_replace('/^\(|\)$/', '', $this->prop['filter']) . ')' . $filter . ')'; |
| | |
| | | |
| | | if ($entry && ($rec = ldap_get_attributes($this->conn, $entry))) |
| | | { |
| | | $this->_debug("S: OK"); |
| | | $this->_debug("S: OK"/* . print_r($rec, true)*/); |
| | | |
| | | $rec = array_change_key_case($rec, CASE_LOWER); |
| | | |
| | |
| | | // Map out the column names to their LDAP ones to build the new entry. |
| | | $newentry = array(); |
| | | $newentry['objectClass'] = $this->prop['LDAP_Object_Classes']; |
| | | foreach ($save_cols as $col => $val) { |
| | | $fld = $this->_map_field($col); |
| | | foreach ($this->fieldmap as $col => $fld) { |
| | | $val = $save_cols[$col]; |
| | | if (is_array($val)) |
| | | $val = array_filter($val); // remove empty entries |
| | | if ($fld && $val) { |
| | | // The field does exist, add it to the entry. |
| | | $newentry[$fld] = $val; |
| | |
| | | } // end foreach |
| | | |
| | | // Verify that the required fields are set. |
| | | // We know that the email address is required as a default of rcube, so |
| | | // we will default its value into any unfilled required fields. |
| | | foreach ($this->prop['required_fields'] as $fld) { |
| | | $missing = null; |
| | | if (!isset($newentry[$fld])) { |
| | | $newentry[$fld] = $newentry[$this->_map_field('email')]; |
| | | } // end if |
| | | } // end foreach |
| | | $missing[] = $fld; |
| | | } |
| | | } |
| | | |
| | | // abort process if requiered fields are missing |
| | | // TODO: generate message saying which fields are missing |
| | | if ($missing) { |
| | | $this->set_error(self::ERROR_INCOMPLETE, 'formincomplete'); |
| | | return false; |
| | | } |
| | | |
| | | // Build the new entries DN. |
| | | $dn = $this->prop['LDAP_rdn'].'='.rcube_ldap::quote_string($newentry[$this->prop['LDAP_rdn']], true) |
| | | .','.$this->prop['base_dn']; |
| | | $dn = $this->prop['LDAP_rdn'].'='.rcube_ldap::quote_string($newentry[$this->prop['LDAP_rdn']], true).','.$this->prop['base_dn']; |
| | | |
| | | $this->_debug("C: Add [dn: $dn]: ".print_r($newentry, true)); |
| | | |
| | | $res = ldap_add($this->conn, $dn, $newentry); |
| | | if ($res === FALSE) { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | | return false; |
| | | } // end if |
| | | |
| | |
| | | $newdata = array(); |
| | | $replacedata = array(); |
| | | $deletedata = array(); |
| | | foreach ($save_cols as $col => $val) { |
| | | $fld = $this->_map_field($col); |
| | | foreach ($this->fieldmap as $col => $fld) { |
| | | $val = $save_cols[$col]; |
| | | if ($fld) { |
| | | // The field does exist compare it to the ldap record. |
| | | if ($record[$col] != $val) { |
| | |
| | | $this->_debug("C: Delete [dn: $dn]: ".print_r($deletedata, true)); |
| | | if (!ldap_mod_del($this->conn, $dn, $deletedata)) { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | | return false; |
| | | } |
| | | $this->_debug("S: OK"); |
| | |
| | | // Handle RDN change |
| | | if ($replacedata[$this->prop['LDAP_rdn']]) { |
| | | $newdn = $this->prop['LDAP_rdn'].'=' |
| | | .rcube_ldap::quote_string($replacedata[$this->prop['LDAP_rdn']], true) |
| | | .','.$this->prop['base_dn']; |
| | | .rcube_ldap::quote_string($replacedata[$this->prop['LDAP_rdn']], true) |
| | | .','.$this->prop['base_dn']; |
| | | if ($dn != $newdn) { |
| | | $newrdn = $this->prop['LDAP_rdn'].'=' |
| | | .rcube_ldap::quote_string($replacedata[$this->prop['LDAP_rdn']], true); |
| | | .rcube_ldap::quote_string($replacedata[$this->prop['LDAP_rdn']], true); |
| | | unset($replacedata[$this->prop['LDAP_rdn']]); |
| | | } |
| | | } |
| | |
| | | if (!ldap_mod_replace($this->conn, $dn, $replacedata)) { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | return false; |
| | | } |
| | | } |
| | | $this->_debug("S: OK"); |
| | | } // end if |
| | | } // end if |
| | |
| | | $this->_debug("C: Add [dn: $dn]: ".print_r($newdata, true)); |
| | | if (!ldap_mod_add($this->conn, $dn, $newdata)) { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | | return false; |
| | | } |
| | | $this->_debug("S: OK"); |
| | |
| | | $res = ldap_delete($this->conn, $dn); |
| | | if ($res === FALSE) { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | | return false; |
| | | } // end if |
| | | $this->_debug("S: OK"); |
| | |
| | | |
| | | $this->_debug("C: Search [".$filter."]"); |
| | | |
| | | if ($this->ldap_result = @$function($this->conn, $this->prop['base_dn'], $filter, array_values($this->fieldmap), 0, 0)) { |
| | | if ($this->ldap_result = @$function($this->conn, $this->prop['base_dn'], $filter, |
| | | array_values($this->fieldmap), 0, (int) $this->prop['sizelimit'], (int) $this->prop['timelimit']) |
| | | ) { |
| | | $this->_debug("S: ".ldap_count_entries($this->conn, $this->ldap_result)." record(s)"); |
| | | return true; |
| | | } else |
| | |
| | | */ |
| | | private function _ldap2result($rec) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $out = array(); |
| | | |
| | | if ($rec['dn']) |
| | |
| | | |
| | | foreach ($this->fieldmap as $rf => $lf) |
| | | { |
| | | if ($rec[$lf]['count']) { |
| | | if ($rf == 'email' && $mail_domain && !strpos($rec[$lf][0], '@')) |
| | | $out[$rf] = sprintf('%s@%s', $rec[$lf][0], $this->mail_domain); |
| | | for ($i=0; $i < $rec[$lf]['count']; $i++) { |
| | | if (!($value = $rec[$lf][$i])) |
| | | continue; |
| | | if ($rf == 'email' && $this->mail_domain && !strpos($value, '@')) |
| | | $out[$rf][] = sprintf('%s@%s', $value, $this->mail_domain); |
| | | else if (in_array($rf, array('street','zipcode','locality','country','region'))) |
| | | $out['address'][$i][$rf] = $value; |
| | | else if ($rec[$lf]['count'] > 1) |
| | | $out[$rf][] = $value; |
| | | else |
| | | $out[$rf] = $rec[$lf][0]; |
| | | $out[$rf] = $value; |
| | | } |
| | | } |
| | | |
| | |
| | | // list of known attribute aliases |
| | | $aliases = array( |
| | | 'gn' => 'givenname', |
| | | 'rfc822mailbox' => 'mail', |
| | | 'rfc822mailbox' => 'email', |
| | | 'userid' => 'uid', |
| | | 'emailaddress' => 'email', |
| | | 'pkcs9email' => 'email', |
| | |
| | | */ |
| | | function quote_string($str, $dn=false) |
| | | { |
| | | // take firt entry if array given |
| | | if (is_array($str)) |
| | | $str = reset($str); |
| | | |
| | | if ($dn) |
| | | $replace = array(','=>'\2c', '='=>'\3d', '+'=>'\2b', '<'=>'\3c', |
| | | '>'=>'\3e', ';'=>'\3b', '\\'=>'\5c', '"'=>'\22', '#'=>'\23'); |
| | |
| | | return strtr($str, $replace); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Setter for the current group |
| | | * (empty, has to be re-implemented by extending class) |
| | | */ |
| | | function set_group($group_id) |
| | | { |
| | | if ($group_id) |
| | | { |
| | | if (! $this->group_cache) $this->list_groups(); |
| | | $cache = $this->group_cache[$group_id]['members']; |
| | | |
| | | $members = array(); |
| | | for ($i=1; $i<$cache["count"]; $i++) |
| | | { |
| | | $member_dn = base64_encode($cache[$i]); |
| | | $members[$member_dn] = 1; |
| | | } |
| | | $this->group_members = $members; |
| | | $this->group_id = $group_id; |
| | | } |
| | | else $this->group_id = 0; |
| | | } |
| | | |
| | | /** |
| | | * List all active contact groups of this source |
| | | * |
| | | * @param string Optional search string to match group name |
| | | * @return array Indexed list of contact groups, each a hash array |
| | | */ |
| | | function list_groups($search = null) |
| | | { |
| | | if (!$this->prop['groups']) |
| | | return array(); |
| | | |
| | | $base_dn = $this->prop['groups']['base_dn']; |
| | | $filter = $this->prop['groups']['filter']; |
| | | |
| | | $res = ldap_search($this->conn, $base_dn, $filter, array('cn','member')); |
| | | if ($res === false) |
| | | { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | | return array(); |
| | | } |
| | | $ldap_data = ldap_get_entries($this->conn, $res); |
| | | |
| | | $groups = array(); |
| | | $group_sortnames = array(); |
| | | for ($i=0; $i<$ldap_data["count"]; $i++) |
| | | { |
| | | $group_name = $ldap_data[$i]['cn'][0]; |
| | | $group_id = base64_encode($group_name); |
| | | $groups[$group_id]['ID'] = $group_id; |
| | | $groups[$group_id]['name'] = $group_name; |
| | | $groups[$group_id]['members'] = $ldap_data[$i]['member']; |
| | | $group_sortnames[] = strtolower($group_name); |
| | | } |
| | | array_multisort($group_sortnames, SORT_ASC, SORT_STRING, $groups); |
| | | |
| | | $this->group_cache = $groups; |
| | | return $groups; |
| | | } |
| | | |
| | | /** |
| | | * Create a contact group with the given name |
| | | * |
| | | * @param string The group name |
| | | * @return mixed False on error, array with record props in success |
| | | */ |
| | | function create_group($group_name) |
| | | { |
| | | if (!$this->group_cache) |
| | | $this->list_groups(); |
| | | |
| | | $base_dn = $this->prop['groups']['base_dn']; |
| | | $new_dn = "cn=$group_name,$base_dn"; |
| | | $new_gid = base64_encode($group_name); |
| | | |
| | | $new_entry = array( |
| | | 'objectClass' => array('top', 'groupOfNames'), |
| | | 'cn' => $group_name, |
| | | 'member' => '', |
| | | ); |
| | | |
| | | $res = ldap_add($this->conn, $new_dn, $new_entry); |
| | | if ($res === false) |
| | | { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | | return false; |
| | | } |
| | | return array('id' => $new_gid, 'name' => $group_name); |
| | | } |
| | | |
| | | /** |
| | | * Delete the given group and all linked group members |
| | | * |
| | | * @param string Group identifier |
| | | * @return boolean True on success, false if no data was changed |
| | | */ |
| | | function delete_group($group_id) |
| | | { |
| | | if (!$this->group_cache) |
| | | $this->list_groups(); |
| | | |
| | | $base_dn = $this->prop['groups']['base_dn']; |
| | | $group_name = $this->group_cache[$group_id]['name']; |
| | | |
| | | $del_dn = "cn=$group_name,$base_dn"; |
| | | $res = ldap_delete($this->conn, $del_dn); |
| | | if ($res === false) |
| | | { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * Rename a specific contact group |
| | | * |
| | | * @param string Group identifier |
| | | * @param string New name to set for this group |
| | | * @return boolean New name on success, false if no data was changed |
| | | */ |
| | | function rename_group($group_id, $new_name) |
| | | { |
| | | if (!$this->group_cache) |
| | | $this->list_groups(); |
| | | |
| | | $base_dn = $this->prop['groups']['base_dn']; |
| | | $group_name = $this->group_cache[$group_id]['name']; |
| | | $old_dn = "cn=$group_name,$base_dn"; |
| | | $new_rdn = "cn=$new_name"; |
| | | |
| | | $res = ldap_rename($this->conn, $old_dn, $new_rdn, NULL, TRUE); |
| | | if ($res === false) |
| | | { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | | return false; |
| | | } |
| | | return $new_name; |
| | | } |
| | | |
| | | /** |
| | | * Add the given contact records the a certain group |
| | | * |
| | | * @param string Group identifier |
| | | * @param array List of contact identifiers to be added |
| | | * @return int Number of contacts added |
| | | */ |
| | | function add_to_group($group_id, $contact_ids) |
| | | { |
| | | if (!$this->group_cache) |
| | | $this->list_groups(); |
| | | |
| | | $base_dn = $this->prop['groups']['base_dn']; |
| | | $group_name = $this->group_cache[$group_id]['name']; |
| | | $group_dn = "cn=$group_name,$base_dn"; |
| | | |
| | | $new_attrs = array(); |
| | | foreach (explode(",", $contact_ids) as $id) |
| | | $new_attrs['member'][] = base64_decode($id); |
| | | |
| | | $res = ldap_mod_add($this->conn, $group_dn, $new_attrs); |
| | | if ($res === false) |
| | | { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | | return 0; |
| | | } |
| | | return count($new_attrs['member']); |
| | | } |
| | | |
| | | /** |
| | | * Remove the given contact records from a certain group |
| | | * |
| | | * @param string Group identifier |
| | | * @param array List of contact identifiers to be removed |
| | | * @return int Number of deleted group members |
| | | */ |
| | | function remove_from_group($group_id, $contact_ids) |
| | | { |
| | | if (!$this->group_cache) |
| | | $this->list_groups(); |
| | | |
| | | $base_dn = $this->prop['groups']['base_dn']; |
| | | $group_name = $this->group_cache[$group_id]['name']; |
| | | $group_dn = "cn=$group_name,$base_dn"; |
| | | |
| | | $del_attrs = array(); |
| | | foreach (explode(",", $contact_ids) as $id) |
| | | $del_attrs['member'][] = base64_decode($id); |
| | | |
| | | $res = ldap_mod_del($this->conn, $group_dn, $del_attrs); |
| | | if ($res === false) |
| | | { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | | return 0; |
| | | } |
| | | return count($del_attrs['member']); |
| | | } |
| | | |
| | | /** |
| | | * Get group assignments of a specific contact record |
| | | * |
| | | * @param mixed Record identifier |
| | | * |
| | | * @return array List of assigned groups as ID=>Name pairs |
| | | * @since 0.5-beta |
| | | */ |
| | | function get_record_groups($contact_id) |
| | | { |
| | | if (!$this->prop['groups']) |
| | | return array(); |
| | | |
| | | $base_dn = $this->prop['groups']['base_dn']; |
| | | $contact_dn = base64_decode($contact_id); |
| | | $filter = "(member=$contact_dn)"; |
| | | |
| | | $res = ldap_search($this->conn, $base_dn, $filter, array('cn')); |
| | | if ($res === false) |
| | | { |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | $this->set_error(self::ERROR_SAVING, 'errorsaving'); |
| | | return array(); |
| | | } |
| | | $ldap_data = ldap_get_entries($this->conn, $res); |
| | | |
| | | $groups = array(); |
| | | for ($i=0; $i<$ldap_data["count"]; $i++) |
| | | { |
| | | $group_name = $ldap_data[$i]['cn'][0]; |
| | | $group_id = base64_encode($group_name); |
| | | $groups[$group_id] = $group_id; |
| | | } |
| | | return $groups; |
| | | } |
| | | } |
| | | |
| | | ?> |