diff --git a/phpgwapi/inc/class.accounts.inc.php b/phpgwapi/inc/class.accounts.inc.php index 829c34e4ac..e4be2f59f7 100644 --- a/phpgwapi/inc/class.accounts.inc.php +++ b/phpgwapi/inc/class.accounts.inc.php @@ -1,783 +1,986 @@ * - * and Bettina Gille [ceb@phpgroupware.org] * - * shared functions for other account repository managers * - * Copyright (C) 2000 - 2002 Joseph Engo * - * Copyright (C) 2003 Joseph Engo, Bettina Gille * - * Caching and documentation added by RalfBecker-AT-outdoor-training.de * - * -------------------------------------------------------------------------* - * This library is part of the eGroupWare API * - * http://www.egroupware.org * - * ------------------------------------------------------------------------ * - * This library is free software; you can redistribute it and/or modify it * - * under the terms of the GNU Lesser General Public License as published by * - * the Free Software Foundation; either version 2.1 of the License, * - * or any later version. * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * See the GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License * - * along with this library; if not, write to the Free Software Foundation, * - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - \**************************************************************************/ +/** + * API - accounts + * + * This class extends a backend class (at them moment SQL or LDAP) and implements some + * caching on to top of the backend functions. The cache is share for all instances of + * the accounts class and for LDAP it is persistent through the whole session, for SQL + * it's only on a per request basis. + * + * @link http://www.egroupware.org + * @author Ralf Becker complete rewrite in 6/2006 and earlier modifications + * + * Implements the (now depricated) interfaces on the former accounts class written by + * Joseph Engo and Bettina Gille + * Copyright (C) 2000 - 2002 Joseph Engo, Copyright (C) 2003 Joseph Engo, Bettina Gille + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @subpackage accounts + * @access public + * @version $Id$ + */ - /* $Id$ */ - - if (empty($GLOBALS['egw_info']['server']['account_repository'])) +// load the backend class, which this class extends +if (empty($GLOBALS['egw_info']['server']['account_repository'])) +{ + if (!empty($GLOBALS['egw_info']['server']['auth_type'])) { - if (!empty($GLOBALS['egw_info']['server']['auth_type'])) + $GLOBALS['egw_info']['server']['account_repository'] = $GLOBALS['egw_info']['server']['auth_type']; + } + else + { + $GLOBALS['egw_info']['server']['account_repository'] = 'sql'; + } +} +include_once(EGW_API_INC . '/class.accounts_' . $GLOBALS['egw_info']['server']['account_repository'] . '.inc.php'); + +/** + * API - accounts + * + * This class extends a backend class (at them moment SQL or LDAP) and implements some + * caching on to top of the backend functions. The cache is share for all instances of + * the accounts class and for LDAP it is persistent through the whole session, for SQL + * it's only on a per request basis. + * + * The backend only implements the read, save, delete, name2id and the {set_}members{hips} methods. + * The account class implements all other (eg. name2id, id2name) functions on top of these. + * + * @link http://www.egroupware.org + * @author Ralf Becker complete rewrite in 6/2006 + * + * Implements the (now depricated) interfaces on the former accounts class written by + * Joseph Engo and Bettina Gille + * Copyright (C) 2000 - 2002 Joseph Engo, Copyright (C) 2003 Joseph Engo, Bettina Gille + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @subpackage accounts + * @access public + * @version $Id$ + */ +class accounts extends accounts_backend +{ + var $xmlrpc_methods = array( + array( + 'name' => 'search', + 'description' => 'Returns a list of accounts and/or groups' + ), + array( + 'name' => 'name2id', + 'description' => 'Cross reference account_lid with account_id' + ), + array( + 'name' => 'id2name', + 'description' => 'Cross reference account_id with account_lid' + ), + array( + 'name' => 'get_list', + 'description' => 'Depricated: use search. Returns a list of accounts and/or groups' + ), + ); + /** + * enables the session-cache, done in the constructor for the LDAP backend only + * + * @var boolean $use_session_cache + */ + var $use_session_cache = false; + + /** + * Depricated: Account this class was instanciated for + * + * @deprecated dont use this in new code, always explcitly specify the account to use + * @var int account_id + */ + var $account_id; + /** + * Depricated: Account data of $this->account_id + * + * @deprecated dont use this in new code, store the data in your own code + * @var unknown_type + */ + var $data; + + /** + * Keys for which both versions with 'account_' prefix and without (depricated!) can be used, if requested. + * Migrate your code to always use the 'account_' prefix!!! + * + * @var array + */ + var $depricated_names = array('firstname','lastname','fullname','email','type', + 'status','expires','lastlogin','lastloginfrom','lastpasswd_change'); + + /** + * Constructor + * + * @param int $account_id=0 account to instanciate the class for (depricated) + */ + function accounts($account_id=0) + { + if($account_id && is_numeric($account_id)) $this->account_id = (int) $account_id; + + $this->accounts_backend(); // call constructor of extended class + } + + /** + * Searches / lists accounts: users and/or groups + * + * @param array with the following keys: + * @param $param['type'] string/int 'accounts', 'groups', 'owngroups' (groups the user is a member of), 'both' + * or integer group-id for a list of members of that group + * @param $param['start'] int first account to return (returns offset or max_matches entries) or all if not set + * @param $param['sort'] string column to sort after, default account_lid if unset + * @param $param['order'] string 'ASC' or 'DESC', default 'DESC' if not set + * @param $param['query'] string to search for, no search if unset or empty + * @param $param['query_type'] string: + * 'all' - query all fields for containing $param[query] + * 'start' - query all fields starting with $param[query] + * 'exact' - query all fields for exact $param[query] + * 'lid','firstname','lastname','email' - query only the given field for containing $param[query] + * @param $param['app'] string with an app-name, to limit result on accounts with run-right for that app + * @param $param['offset'] int - number of matches to return if start given, default use the value in the prefs + * @return array with uid / data pairs, data is an array with account_id, account_lid, account_firstname, + * account_lastname, person_id (id of the linked addressbook entry), account_status, account_expires, account_primary_group + */ + function search($param) + { + //echo "

accounts::search(".print_r($param,True).")

\n"; + $this->setup_cache(); + $account_search = &$this->cache['account_search']; + + $serial = serialize($param); + + if (isset($account_search[$serial])) { - $GLOBALS['egw_info']['server']['account_repository'] = $GLOBALS['egw_info']['server']['auth_type']; + $this->total = $account_search[$serial]['total']; + } + elseif (method_exists('accounts_','search')) // implements its on search function ==> use it + { + $account_search[$serial]['data'] = parent::search($param); + $account_search[$serial]['total'] = $this->total; } else { - $GLOBALS['egw_info']['server']['account_repository'] = 'sql'; - } - } - include_once(EGW_API_INC . '/class.accounts_' . $GLOBALS['egw_info']['server']['account_repository'] . '.inc.php'); - - /* - Dont know where to put this (seek3r) - This is where it belongs (jengo) - This is where it ended up (milosch) - Moved again at least temporarily since sql and ldap use it. - */ - $GLOBALS['egw_info']['server']['global_denied_users'] = array( - 'root' => True, 'bin' => True, 'daemon' => True, - 'adm' => True, 'lp' => True, 'sync' => True, - 'shutdown' => True, 'halt' => True, 'ldap' => True, - 'mail' => True, 'news' => True, 'uucp' => True, - 'operator' => True, 'games' => True, 'gopher' => True, - 'nobody' => True, 'xfs' => True, 'pgsql' => True, - 'mysql' => True, 'postgres' => True, 'oracle' => True, - 'ftp' => True, 'gdm' => True, 'named' => True, - 'alias' => True, 'web' => True, 'sweep' => True, - 'cvs' => True, 'qmaild' => True, 'qmaill' => True, - 'qmaillog' => True, 'qmailp' => True, 'qmailq' => True, - 'qmailr' => True, 'qmails' => True, 'rpc' => True, - 'rpcuser' => True, 'amanda' => True, 'apache' => True, - 'pvm' => True, 'squid' => True, 'ident' => True, - 'nscd' => True, 'mailnull' => True, 'cyrus' => True, - 'backup' => True - ); - - $GLOBALS['egw_info']['server']['global_denied_groups'] = array( - 'root' => True, 'bin' => True, 'daemon' => True, - 'sys' => True, 'adm' => True, 'tty' => True, - 'disk' => True, 'lp' => True, 'mem' => True, - 'kmem' => True, 'wheel' => True, 'mail' => True, - 'uucp' => True, 'man' => True, 'games' => True, - 'dip' => True, 'ftp' => True, 'nobody' => True, - 'floppy' => True, 'xfs' => True, 'console' => True, - 'utmp' => True, 'pppusers' => True, 'popusers' => True, - 'slipusers' => True, 'slocate' => True, 'mysql' => True, - 'dnstools' => True, 'web' => True, 'named' => True, - 'dba' => True, 'oinstall' => True, 'oracle' => True, - 'gdm' => True, 'sweep' => True, 'cvs' => True, - 'postgres' => True, 'qmail' => True, 'nofiles' => True, - 'ldap' => True, 'backup' => True - ); - - /** - * Class for handling user and group accounts - * - * The class can be instanciated for a certain account, if no account is specified the user of the session is used. - * Some functions operate on that user! - */ - class accounts extends accounts_ - { - var $memberships = array(); - var $members = array(); - var $xmlrpc_methods = array(); - // enables the session-cache - var $use_session_cache = True; - - /** - * Standard constructor for setting $this->account_id - * - * This constructor sets the account id, if string is sent, converts to id - * - * @param int/string $account_id account to instanciate the class for, default user of the session - * @param string $account_type type ('u' or 'g') to set $this->account_type if given - */ - function accounts($account_id = '', $account_type='') - { - // enable the caching in the session onyl for ldap - $this->use_session_cache = $GLOBALS['egw_info']['server']['account_repository'] == 'ldap'; - - if (is_object($GLOBALS['egw_setup']->db)) + $serial2 = $serial; + if (is_numeric($param['type']) || $param['app'] || $param['type'] == 'owngroups') // do we need to limit the search on a group or app? { - $this->db = clone($GLOBALS['egw_setup']->db); + $app = $param['app']; + unset($param['app']); + if (is_numeric($param['type'])) + { + $group = (int) $param['type']; + $param['type'] = 'accounts'; + } + elseif ($param['type'] == 'owngroups') + { + $group = true; + $param['type'] = 'groups'; + } + $start = $param['start']; + unset($param['start']); + $serial2 = serialize($param); + } + if (!isset($account_search[$serial2])) // check if we already did this general search + { + $account_search[$serial2]['data'] = array(); + $accounts = parent::get_list($param['type'],$param['start'],$param['sort'],$param['order'],$param['query'],$param['offset'],$param['query_type']); + if (!$accounts) $accounts = array(); + foreach($accounts as $data) + { + $account_search[$serial2]['data'][$data['account_id']] = $data; + } + $account_search[$serial2]['total'] = $this->total; } else { - $this->db = clone($GLOBALS['egw']->db); + $this->total = $account_search[$serial2]['total']; } - $this->db->set_app('phpgwapi'); // to load the right table-definitions for insert, select, update, ... - - if($account_id != '') + //echo "parent::get_list($param[type],$param[start],$param[sort],$param[order],$param[query],$param[offset],$param[query_type]) returned
".print_r($account_search[$serial2],True)."
\n"; + if ($app || $group) // limit the search on accounts with run-rights for app or a group { - $this->account_id = get_account_id($account_id); - } - - if($account_type != '') - { - $this->account_type = $account_type; - } - - $this->query_types = array( - 'all' => 'all fields', - 'firstname' => 'firstname', - 'lastname' => 'lastname', - 'lid' => 'LoginID', - 'email' => 'email', - 'start' => 'start with', - 'exact' => 'exact', - ); - $this->accounts_(); // call constructor of extended class - - $this->xmlrpc_methods[] = array( - 'name' => 'get_list', - 'description' => 'Returns a list of accounts and/or groups' - ); - $this->xmlrpc_methods[] = array( - 'name' => 'name2id', - 'description' => 'Cross reference account_lid with account_id' - ); - $this->xmlrpc_methods[] = array( - 'name' => 'id2name', - 'description' => 'Cross reference account_id with account_lid' - ); - } - - /** - * Sets up the account-data cache - * - * The cache is shared between all instances of the account-class and it can be save in the session, - * if use_session_cache is set to True - */ - function setup_cache() - { - if ($this->use_session_cache && // are we supposed to use a session-cache - !@$GLOBALS['egw_info']['accounts']['session_cache_setup'] && // is it already setup - // is the account-class ready (startup !) - is_object($GLOBALS['egw']->session) && $GLOBALS['egw']->session->account_id) - { - // setting up the session-cache - $GLOBALS['egw_info']['accounts']['cache'] = $GLOBALS['egw']->session->appsession('accounts_cache','phpgwapi'); - $GLOBALS['egw_info']['accounts']['session_cache_setup'] = True; - //echo "accounts::setup_cache() cache=
".print_r($GLOBALS['egw_info']['accounts']['cache'],True)."
\n"; - } - if (!isset($this->cache)) - { - $this->cache = &$GLOBALS['egw_info']['accounts']['cache']; - } - if (!is_array($this->cache)) $this->cache = array(); - } - - /** - * Saves the account-data cache in the session - * - * Gets called from common::phpgw_final() - */ - function save_session_cache() - { - if ($this->use_session_cache && // are we supposed to use a session-cache - $GLOBALS['egw_info']['accounts']['session_cache_setup'] && // is it already setup - // is the account-class ready (startup !) - is_object($GLOBALS['egw']->session)) - { - $GLOBALS['egw']->session->appsession('accounts_cache','phpgwapi',$GLOBALS['egw_info']['accounts']['cache']); - } - } - - /** - * Searches / lists accounts: users and/or groups - * - * @param array with the following keys: - * @param $param['type'] string/int 'accounts', 'groups', 'owngroups' (groups the user is a member of), 'both' - * or integer group-id for a list of members of that group - * @param $param['start'] int first account to return (returns offset or max_matches entries) or all if not set - * @param $param['sort'] string column to sort after, default account_lid if unset - * @param $param['order'] string 'ASC' or 'DESC', default 'DESC' if not set - * @param $param['query'] string to search for, no search if unset or empty - * @param $param['query_type'] string: - * 'all' - query all fields for containing $param[query] - * 'start' - query all fields starting with $param[query] - * 'exact' - query all fields for exact $param[query] - * 'lid','firstname','lastname','email' - query only the given field for containing $param[query] - * @param $param['app'] string with an app-name, to limit result on accounts with run-right for that app - * @param $param['offset'] int - number of matches to return if start given, default use the value in the prefs - * @return array with uid / data pairs, data is an array with account_id, account_lid, account_firstname, - * account_lastname, person_id (id of the linked addressbook entry), account_status, account_expires, account_primary_group - */ - function search($param) - { - //echo "

accounts::search(".print_r($param,True).")

\n"; - $this->setup_cache(); - $account_search = &$this->cache['account_search']; - - $serial = serialize($param); - - if (isset($account_search[$serial])) - { - $this->total = $account_search[$serial]['total']; - } - elseif (function_exists('accounts_::search')) // implements its on search function ==> use it - { - $account_search[$serial]['data'] = accounts_::search($param); + $valid = array(); + if ($app) + { + $valid = $this->split_accounts($app,$param['type'] == 'both' ? 'merge' : $param['type']); + } + if ($group) + { + $members = is_int($group) ? $this->members($group,true) : $this->memberships($GLOBALS['egw_info']['user']['account_id'],true); + if (!$members) $members = array(); + $valid = !$app ? $members : array_intersect($valid,$members); // use the intersection + } + //echo "

limiting result to app='app' and/or group=$group valid-ids=".print_r($valid,true)."

\n"; + $offset = $param['offset'] ? $param['offset'] : $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']; + $stop = $start + $offset; + $n = 0; + $account_search[$serial]['data'] = array(); + foreach ($account_search[$serial2]['data'] as $id => $data) + { + if (!in_array($id,$valid)) + { + $this->total--; + continue; + } + // now we have a valid entry + if (!is_int($start) || $start <= $n && $n < $stop) + { + $account_search[$serial]['data'][$id] = $data; + } + $n++; + } $account_search[$serial]['total'] = $this->total; } - else + } + //echo "

accounts::search('$serial')=

".print_r($account_search[$serial]['data'],True).")
\n"; + return $account_search[$serial]['data']; + } + + /** + * Reads the data of one account + * + * It's depricated to use read with out parameter to read the internal data of this class!!! + * All key of the returned array use the 'account_' prefix. + * For backward compatibility some values are additionaly availible without the prefix, using them is depricated! + * + * @param int/string $id numeric account_id or string with account_lid (use of default value of 0 is depricated!!!) + * @param boolean $set_depricated_names=false set _additionaly_ the depricated keys without 'account_' prefix + * @return array/boolean array with account data (keys: account_id, account_lid, ...) or false if account not found + */ + function read($id=0,$set_depricated_names=false) + { + if (!$id) // deprecated use!!! + { + return $this->data ? $this->data : $this->read_repository(); + } + if (!is_int($id) && !is_numeric($id)) + { + $id = $this->name2id($id); + } + if (!$id) return false; + + $this->setup_cache(); + $account_data = &$this->cache['account_data']; + + if (!isset($account_data[$id])) + { + $account_data[$id] = parent::read($id); + } + if (!$account_data[$id] || !$set_depricated_names) + { + return $account_data[$id]; + } + $data = $account_data[$id]; + foreach($this->depricated_names as $name) + { + $data[$name] =& $data['account_'.$name]; + } + return $data; + } + + /** + * Saves / adds the data of one account + * + * If no account_id is set in data the account is added and the new id is set in $data. + * + * @param array $data array with account-data + * @param boolean $check_depricated_names=false check _additionaly_ the depricated keys without 'account_' prefix + * @return int/boolean the account_id or false on error + */ + function save(&$data,$check_depricated_names=false) + { + $this->cache_invalidate($data['account_id']); + + if ($check_depricated_names) + { + foreach($this->depricated_names as $name) { - $serial2 = $serial; - if (is_numeric($param['type']) || $param['app'] || $param['type'] == 'owngroups') // do we need to limit the search on a group or app? + if (isset($data[$name]) && !isset($data['account_'.$name])) { - $app = $param['app']; - unset($param['app']); - if (is_numeric($param['type'])) - { - $group = (int) $param['type']; - $param['type'] = 'accounts'; - } - elseif ($param['type'] == 'owngroups') - { - $group = true; - $param['type'] = 'groups'; - } - $start = $param['start']; - unset($param['start']); - $serial2 = serialize($param); - } - if (!isset($account_search[$serial2])) // check if we already did this general search - { - $account_search[$serial2]['data'] = array(); - $accounts = accounts_::get_list($param['type'],$param['start'],$param['sort'],$param['order'],$param['query'],$param['offset'],$param['query_type']); - if (!$accounts) $accounts = array(); - foreach($accounts as $data) - { - $account_search[$serial2]['data'][$data['account_id']] = $data; - } - $account_search[$serial2]['total'] = $this->total; - } - else - { - $this->total = $account_search[$serial2]['total']; - } - //echo "accounts_::get_list($param[type],$param[start],$param[sort],$param[order],$param[query],$param[offset],$param[query_type]) returned
".print_r($account_search[$serial2],True)."
\n"; - if ($app || $group) // limit the search on accounts with run-rights for app or a group - { - $valid = array(); - if ($app) - { - $valid = $this->split_accounts($app,$param['type'] == 'both' ? 'merge' : $param['type']); - } - if ($group) - { - $members = is_int($group) ? $GLOBALS['egw']->acl->get_ids_for_location($group, 1, 'phpgw_group') : - $GLOBALS['egw']->acl->get_location_list_for_id('phpgw_group', 1,$GLOBALS['egw_info']['user']['account_id']); - if (!$members) $members = array(); - $valid = !$app ? $members : array_intersect($valid,$members); // use the intersection - } - //echo "

limiting result to app='app' and/or group=$group valid-ids=".print_r($valid,true)."

\n"; - $offset = $param['offset'] ? $param['offset'] : $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']; - $stop = $start + $offset; - $n = 0; - $account_search[$serial]['data'] = array(); - foreach ($account_search[$serial2]['data'] as $id => $data) - { - if (!in_array($id,$valid)) - { - $this->total--; - continue; - } - // now we have a valid entry - if (!is_int($start) || $start <= $n && $n < $stop) - { - $account_search[$serial]['data'][$id] = $data; - } - $n++; - } - $account_search[$serial]['total'] = $this->total; + $data['account_'.$name] =& $data[$name]; } } - //echo "

accounts::search('$serial')=

".print_r($account_search[$serial]['data'],True).")
\n"; - return $account_search[$serial]['data']; } - - /** - * Searches / lists accounts: users and/or groups - * - * @deprecated use search - */ - function get_list($_type='both',$start = '',$sort = '', $order = '', $query = '', $offset = '',$query_type='') + if (($id = parent::save($data)) && $data['account_primary_group'] && + (!($memberships = $this->memberships($id,true)) || !in_array($data['account_primary_group'],$memberships))) { - //echo "

accounts::get_list(".print_r($_type,True).",start='$start',sort='$sort',order='$order',query='$query',offset='$offset')

\n"; - $this->setup_cache(); - $account_list = &$this->cache['account_list']; - - // For XML-RPC - if (is_array($_type)) - { - $p = $_type; - $_type = $p['type']; - $start = $p['start']; - $order = $p['order']; - $query = $p['query']; - $offset = $p['offset']; - $query_type = $p['query_type']; - } - else - { - $p = array( - 'type' => $_type, - 'start' => $start, - 'order' => $order, - 'query' => $query, - 'offset' => $offset, - 'query_type' => $query_type , - ); - } - $serial = serialize($p); - - if (isset($account_list[$serial])) - { - $this->total = $account_list[$serial]['total']; - } - else - { - $account_list[$serial]['data'] = accounts_::get_list($_type,$start,$sort,$order,$query,$offset,$query_type); - $account_list[$serial]['total'] = $this->total; - } - return $account_list[$serial]['data']; + $memberships[] = $data['account_primary_group']; + $this->set_memberships($memberships,$id); } - - /** - * test if the account this class is instanciated for is expired - * - * @return boolean true=expired (no more login possible), false otherwise - */ - function is_expired() + return $id; + } + + /** + * Delete one account, deletes also all acl-entries for that account + * + * @param int/string $id numeric account_id or string with account_lid + * @return boolean true on success, false otherwise + */ + function delete($id) + { + if (!is_int($id) && !is_numeric($id)) { - if ($this->data['expires'] != -1 && $this->data['expires'] < time()) - { - return True; - } - else - { - return False; - } + $id = $this->name2id($id); } + if (!$id) return false; - /** - * Invalidate the cache (or parts of it) after change in $account_id - * - * Atm simplest approach - delete it all ;-) - */ - function cache_invalidate($account_id) + $this->cache_invalidate($id); + parent::delete($id); + + // delete all acl_entries belonging to that user or group + $GLOBALS['egw']->acl->delete_account($id); + + return true; + } + + /** + * test if an account is expired + * + * @param array $data=null array with account data, not specifying the account is depricated!!! + * @return boolean true=expired (no more login possible), false otherwise + */ + function is_expired($data=null) + { + if (is_null($data)) $data = $this->data; // depricated use + + $expires = isset($data['account_expires']) ? $data['account_expires'] : $data['expires']; +echo "

is_expired(".print_r($data,true).") expires=$expires --> ".(int)($expires != -1 && $expires < time())."

\n"; +echo function_backtrace(); + return $expires != -1 && $expires < time(); + } + + /** + * convert an alphanumeric account-value (account_lid, account_email) to the account_id + * + * Please note: + * - if a group and an user have the same account_lid the group will be returned (LDAP only) + * - if multiple user have the same email address, the returned user is undefined + * + * @param string $name value to convert + * @param string $which='account_lid' type of $name: account_lid (default), account_email, person_id, account_fullname + * @param string $account_type=null u = user or g = group, or default null = try both + * @return int/false numeric account_id or false on error ($name not found) + */ + function name2id($name,$which='account_lid',$account_type=null) + { + $this->setup_cache(); + $name_list = &$this->cache['name_list']; + + if(@isset($name_list[$which][$name]) && $name_list[$which][$name]) { - //echo "

accounts::cache_invalidate($account_id)

\n"; - $GLOBALS['egw_info']['accounts']['cache'] = array(); - - if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) // egw object in setup is limited - { - $GLOBALS['egw']->invalidate_session_cache(); // invalidates whole egw-enviroment if stored in the session - } + return $name_list[$which][$name]; } - /** - * saves the account-data in the internal data-structure of this class to the repository - */ - function save_repository() + // Don't bother searching for empty account_lid + if(empty($name)) { - $this->cache_invalidate($this->account_id); - accounts_::save_repository(); + return False; } + return $name_list[$which][$name] = parent::name2id($name,$which,$account_type); + } - /** - * Deletes the account spezified by $accountid, deletes also all acl-entries for that account - * - * @param int $accountid numeric account-id - */ - function delete($accountid) + /** + * Convert an numeric account_id to any other value of that account (account_lid, account_email, ...) + * + * Uses the read method to fetch all data. + * + * @param int $account_id numerica account_id + * @param string $which='account_lid' type to convert to: account_lid (default), account_email, ... + * @return string/false converted value or false on error ($account_id not found) + */ + function id2name($account_id,$which='account_lid') + { + if (!($data = $this->read($account_id))) return false; + + return $data[$which]; + + $this->setup_cache(); + $id_list = &$this->cache['id_list']; + } + + /** + * get the type of an account: 'u' = user, 'g' = group + * + * @param int/string $accountid numeric account-id or alphanum. account-lid, + * if !$accountid account of the user of this session + * @return string/false 'u' = user, 'g' = group or false on error ($accountid not found) + */ + function get_type($account_id) + { + if (!is_int($account_id) && !is_numeric($account_id)) { - $this->cache_invalidate($accountid); - accounts_::delete($accountid); - - // delete all acl_entries belonging to that user or group - $GLOBALS['egw']->acl->delete_account($accountid); + $account_id = $this->name2id($account_id); } - - /** - * Create a new account with the given $account_info - * - * @param array data for the new account - * @return int new nummeric account-id - */ - function create($account_info,$default_prefs=True) + return $account_id > 0 ? 'u' : ($account_id < 0 ? 'g' : false); + } + + /** + * check if an account exists and if it is an user or group + * + * @param int/string $account_id numeric account_id or account_lid + * @return int 0 = acount does not exist, 1 = user, 2 = group + */ + function exists($account_id) + { + if (!($data = $this->read($account_id))) { - $account_id = accounts_::create($account_info,$default_prefs); - $this->cache_invalidate($account_id); - - return $account_id; + return 0; } + return $data['account_type'] == 'u' ? 1 : 2; + } - /** - * Reads the data of the account this class is instanciated for - * - * @return array with the internal data - */ - function read_repository() + /** + * Get all memberships of an account $account_id / groups the account is a member off + * + * @param int/string $account_id numeric account-id or alphanum. account-lid + * @param boolean $just_id=false return just account_id's or account_id => account_lid pairs + * @return array with account_id's ($just_id) or account_id => account_lid pairs (!$just_id) + */ + function memberships($account_id,$just_id=false) + { + $this->setup_cache(); + $memberships_list = &$this->cache['memberships_list']; + + if (!is_int($account_id) && !is_numeric($account_id)) + { + $account_id = $this->name2id($account_id,'account_lid','u'); + } + if (!isset($memberships_list[$account_id])) + { + $memberships_list[$account_id] = parent::memberships($account_id); + } + //echo "accounts::memberships($account_id)"; _debug_array($memberships_list[$account_id]); + return $just_id && $memberships_list[$account_id] ? array_keys($memberships_list[$account_id]) : $memberships_list[$account_id]; + } + + /** + * Sets the memberships of a given account + * + * @param array $groups array with gidnumbers + * @param int $account_id uidnumber + */ + function set_memberships($groups,$account_id) + { + if (!is_int($account_id) && !is_numeric($account_id)) + { + $account_id = $this->name2id($account_id); + } + parent::set_memberships($groups,$account_id); + + $this->cache_invalidate($account_id); + } + + /** + * Get all members of the group $account_id + * + * @param int/string $accountid='' numeric account-id or alphanum. account-lid, + * default account of the user of this session + * @param boolean $just_id=false return just an array of id's and not id => lid pairs, default false + * @return array with account_id ($just_id) or account_id => account_lid pairs (!$just_id) + */ + function members($account_id,$just_id=false) + { + $this->setup_cache(); + $members_list = &$this->cache['members_list']; + + if (!is_int($account_id) && !is_numeric($account_id)) + { + $account_id = $this->name2id($account_id); + } + if (!isset($members_list[$account_id])) + { + $members_list[$account_id] = parent::members($account_id); + } + //echo "accounts::members($account_id)"; _debug_array($members_list[$account_id]); + return $just_id && $members_list[$account_id] ? array_keys($members_list[$account_id]) : $members_list[$account_id]; + } + + /** + * Set the members of a group + * + * @param array $members array with uidnumber or uid's + * @param int $gid gidnumber of group to set + */ + function set_members($members,$gid) + { + //echo "

accounts::set_members(".print_r($members,true).",$gid)

\n"; + parent::set_members($members,$gid); + + $this->cache_invalidate(0); + } + + /** + * splits users and groups from a array of id's or the accounts with run-rights for a given app-name + * + * @param array $app_users array of user-id's or app-name (if you use app-name the result gets cached!) + * @param string $use what should be returned only an array with id's of either 'accounts' or 'groups'. + * Or an array with arrays for 'both' under the keys 'groups' and 'accounts' or 'merge' for accounts + * and groups merged into one array + * @return array/boolean see $use, false on error (wront $use) + */ + function split_accounts($app_users,$use='both') + { + if (!is_array($app_users)) { $this->setup_cache(); - $account_data = &$this->cache['account_data']; + $cache = &$this->cache['account_split'][$app_user]; - if (isset($account_data[$this->account_id])) + if (is_array($cache)) { - return $this->data = $account_data[$this->account_id]; + return $cache; } - return $account_data[$this->account_id] = accounts_::read_repository(); + $app_users = $GLOBALS['egw']->acl->get_ids_for_location('run',1,$app_users); } - - /** - * Return data of the account this class is instanciated for - * - * only calls read_repository if no data read so far - * - * @return array with the internal data - */ - function read() + $accounts = array( + 'accounts' => array(), + 'groups' => array(), + ); + foreach($app_users as $id) { - if (count($this->data) == 0) + $type = $this->get_type($id); + if($type == 'g') { - $this->read_repository(); - } - - reset($this->data); - return $this->data; - } - - /** - * copies the given $data into the internal array - * - * @param array with data - * @return array $this->data = $data - */ - function update_data($data) - { - reset($data); - $this->data = Array(); - $this->data = $data; - - reset($this->data); - return $this->data; - } - - /** - * Get all memberships of an account $accountid / groups the account is a member off - * - * @param int/string $accountid='' numeric account-id or alphanum. account-lid, - * default account of the user of this session - * @return array or arrays with keys 'account_id' and 'account_name' for the groups $accountid is a member of - */ - function membership($accountid = '') - { - $this->setup_cache(); - $membership_list = &$this->cache['membership_list']; - - $account_id = get_account_id($accountid); - - if (isset($membership_list[$account_id])) - { - return $membership_list[$account_id]; - } - - if(!($gids = $GLOBALS['egw']->acl->get_location_list_for_id('phpgw_group', 1, $account_id))) - { - return $membership_list[$account_id] = False; - } - - $memberships = Array(); - foreach($gids as $gid) - { - $memberships[] = Array('account_id' => $gid, 'account_name' => $this->id2name($gid)); - } - return $membership_list[$account_id] = $memberships; - } - - /** - * Get all members of the group $accountid - * - * @param int/string $accountid='' numeric account-id or alphanum. account-lid, - * default account of the user of this session - * @return array or arrays with keys 'account_id' and 'account_name' - */ - function member($accountid = '') - { - $this->setup_cache(); - $member_list = &$this->cache['member_list']; - - $account_id = get_account_id($accountid); - - if (isset($member_list[$account_id])) - { - return $member_list[$account_id]; - } - - if (!($uids = $GLOBALS['egw']->acl->get_ids_for_location($account_id, 1, 'phpgw_group'))) - { - return $member_list[$account_id] = False; - } - - $members = array(); - foreach ($uids as $uid) - { - $members[] = Array('account_id' => $uid, 'account_name' => $this->id2name($uid)); - } - return $member_list[$account_id] = $members; - } - - /** - * Using the common functions next_id and last_id, find the next available account_id - * - * NOTE: only used for the creation of LDAP accounts - * - * @param $string $account_type='u' (optional, default to 'u') - * @return int/boolean interger account_id or false if none is free anymore - */ - function get_nextid($account_type='u') - { - $min = $GLOBALS['egw_info']['server']['account_min_id'] ? $GLOBALS['egw_info']['server']['account_min_id'] : 0; - $max = $GLOBALS['egw_info']['server']['account_max_id'] ? $GLOBALS['egw_info']['server']['account_max_id'] : 0; - - if ($account_type == 'g') - { - $type = 'groups'; - $sign = -1; - } - else - { - $type = 'accounts'; - $sign = 1; - } - /* Loop until we find a free id */ - do - { - $account_id = (int) $GLOBALS['egw']->common->next_id($type,$min,$max); - } - while ($account_id && $this->exists($sign * $account_id)); // check need to include the sign! - - if (!$account_id || $GLOBALS['egw_info']['server']['account_max_id'] && - $account_id > $GLOBALS['egw_info']['server']['account_max_id']) - { - return False; - } - return $account_id; - } - - /** - * splits users and groups from a array of id's or the accounts with run-rights for a given app-name - * - * @param array $app_users array of user-id's or app-name (if you use app-name the result gets cached!) - * @param string $use what should be returned only an array with id's of either 'accounts' or 'groups'. - * Or an array with arrays for 'both' under the keys 'groups' and 'accounts' or 'merge' for accounts - * and groups merged into one array - * @return array/boolean see $use, false on error (wront $use) - */ - function split_accounts($app_users,$use='both') - { - if (!is_array($app_users)) - { - $this->setup_cache(); - $cache = &$this->cache['account_split'][$app_user]; - - if (is_array($cache)) - { - return $cache; - } - $app_users = $GLOBALS['egw']->acl->get_ids_for_location('run',1,$app_users); - } - $accounts = array( - 'accounts' => array(), - 'groups' => array(), - ); - foreach($app_users as $id) - { - $type = $this->get_type($id); - if($type == 'g') - { - $accounts['groups'][$id] = $id; - foreach((array)$GLOBALS['egw']->acl->get_ids_for_location($id,1,'phpgw_group') as $id) - { - $accounts['accounts'][$id] = $id; - } - } - else + $accounts['groups'][$id] = $id; + foreach($this->members($id,true) as $id) { $accounts['accounts'][$id] = $id; } } - - // not sure why they need to be sorted, but we need to remove the keys anyway - sort($accounts['groups']); - sort($accounts['accounts']); - - if (isset($cache)) + else { - $cache = $accounts; + $accounts['accounts'][$id] = $id; } - //echo "

accounts::split_accounts(".print_r($app_users,True).",'$use') =

".print_r($accounts,True)."
\n"; - - switch($use) - { - case 'both': - return $accounts; - case 'groups': - return $accounts['groups']; - case 'accounts': - return $accounts['accounts']; - case 'merge': - return array_merge($accounts['accounts'],$accounts['groups']); - } - return False; } - /** - * phpgw compatibility function, better use split_accounts - * - * @deprecated use split_accounts - */ - function return_members($accounts) - { - $arr = $this->split_accounts($accounts); + // not sure why they need to be sorted, but we need to remove the keys anyway + sort($accounts['groups']); + sort($accounts['accounts']); - return array( - 'users' => $arr['accounts'], - 'groups' => $arr['groups'], - ); + if (isset($cache)) + { + $cache = $accounts; } + //echo "

accounts::split_accounts(".print_r($app_users,True).",'$use') =

".print_r($accounts,True)."
\n"; - /** - * convert an alphanumeric account-value (account_lid, account_email) to the account_id - * - * @param string $name value to convert - * @param string $which='account_lid' type of $name: account_lid (default), account_email, person_id, account_fullname - * @return int/false numeric account_id or false on error ($name not found) - */ - function name2id($name,$which='account_lid') + switch($use) { - $this->setup_cache(); - $name_list = &$this->cache['name_list']; - - if(@isset($name_list[$which][$name]) && $name_list[$which][$name]) - { - return $name_list[$which][$name]; - } - - /* Don't bother searching for empty account_lid */ - if(empty($name)) - { - return False; - } - return $name_list[$which][$name] = accounts_::name2id($name,$which); + case 'both': + return $accounts; + case 'groups': + return $accounts['groups']; + case 'accounts': + return $accounts['accounts']; + case 'merge': + return array_merge($accounts['accounts'],$accounts['groups']); } + return False; + } - /** - * convert an numeric account_id to any other value of that account (account_lid, account_email, ...) - * - * @param int $account_id numerica account_id - * @param string $which='account_lid' type to convert to: account_lid (default), account_email, ... - * @return string/false converted value or false on error ($account_id not found) - */ - function id2name($account_id,$which='account_lid') + /** + * Invalidate the cache (or parts of it) after change in $account_id + * + * Atm simplest approach - delete it all ;-) + * + * @param int $account_id for which account_id should the cache be invalid, default 0 = all + */ + function cache_invalidate($account_id=0) + { + //echo "

accounts::cache_invalidate($account_id)

\n"; + $GLOBALS['egw_info']['accounts']['cache'] = array(); + + if (method_exists($GLOBALS['egw'],'invalidate_session_cache')) // egw object in setup is limited { - $this->setup_cache(); - $id_list = &$this->cache['id_list']; - - if (! $account_id) - { - return False; - } - - if($id_list[$account_id][$which]) - { - return $id_list[$account_id][$which]; - } - return $id_list[$account_id][$which] = accounts_::id2name($account_id,$which); - } - - /** - * get the type of an account: 'u' = user, 'g' = group - * - * @param int/string $accountid numeric account-id or alphanum. account-lid, - * if !$accountid account of the user of this session - * @return string/false 'u' = user, 'g' = group or false on error ($accountid not found) - */ - function get_type($accountid) - { - $account_id = get_account_id($accountid); - - return $account_id > 0 ? 'u' : ($account_id < 0 ? 'g' : false); - } - - /** - * Gets account-name (lid), firstname and lastname of an account $accountid - * - * @param int/string $accountid='' numeric account-id or alphanum. account-lid, - * if !$accountid account of the user of this session - * @param string &$lid on return: alphanumeric account-name (lid) - * @param string &$fname on return: first name - * @param string &$lname on return: last name - * @return boolean true if $accountid was found, false otherwise - */ - function get_account_name($accountid,&$lid,&$fname,&$lname) - { - $this->setup_cache(); - $account_name = &$this->cache['account_name']; - - $account_id = get_account_id($accountid); - if(isset($account_name[$account_id])) - { - $lid = $account_name[$account_id]['lid']; - $fname = $account_name[$account_id]['fname']; - $lname = $account_name[$account_id]['lname']; - return $account_name[$account_id] !== False; - } - $Ok = accounts_::get_account_name($accountid,$lid,$fname,$lname); - - if (empty($fname)) $fname = $lid; - if (empty($lname)) $lname = $this->get_type($accountid) == 'g' ? lang('Group') : lang('user'); - - $account_name[$account_id] = array( - 'lid' => $lid, - 'fname' => $fname, - 'lname' => $lname, - ); - return $Ok; - } - - /** - * Reads account-data for a given $account_id from the repository AND sets the class-vars with it - * - * Same effect as instanciating the class with that account, dont do it with $GLOBALS['egw']->account !!! - * - * @param int $accountid numeric account-id - * @return array with keys lid, firstname, lastname, fullname, type - */ - function get_account_data($account_id) - { - $this->account_id = $account_id; - $this->read_repository(); - - $data[$this->data['account_id']]['lid'] = $this->data['account_lid']; - $data[$this->data['account_id']]['firstname'] = $this->data['firstname']; - $data[$this->data['account_id']]['lastname'] = $this->data['lastname']; - $data[$this->data['account_id']]['fullname'] = $this->data['fullname']; - $data[$this->data['account_id']]['type'] = $this->data['account_type']; - - return $data; + $GLOBALS['egw']->invalidate_session_cache(); // invalidates whole egw-enviroment if stored in the session } } + + /** + * Add an account for an authenticated user + * + * Expiration date and primary group are read from the system configuration. + * + * @param string $account_lid + * @param string $passwd + * @param array $GLOBALS['auto_create_acct'] values for 'firstname', 'lastname', 'email' and 'primary_group' + * @return int/boolean account_id or false on error + */ + function auto_add($account_lid, $passwd) + { + $expires = !isset($GLOBALS['egw_info']['server']['auto_create_expire']) || + $GLOBALS['egw_info']['server']['auto_create_expire'] == 'never' ? -1 : + time() + $GLOBALS['egw_info']['server']['auto_create_expire'] + 2; + + + if (!($default_group_id = $this->name2id($GLOBALS['egw_info']['server']['default_group_lid']))) + { + $default_group_id = $this->name2id('Default'); + } + $primary_group = $GLOBALS['auto_create_acct']['primary_group'] && + $this->get_type((int)$GLOBALS['auto_create_acct']['primary_group']) === 'g' ? + (int)$GLOBALS['auto_create_acct']['primary_group'] : $default_group_id; + + $data = array( + 'account_lid' => $account_lid, + 'account_type' => 'u', + 'account_passwd' => $passwd, + 'account_firstname' => $GLOBALS['auto_create_acct']['firstname'] ? $GLOBALS['auto_create_acct']['firstname'] : 'New', + 'account_lastname' => $GLOBALS['auto_create_acct']['lastname'] ? $GLOBALS['auto_create_acct']['lastname'] : 'User', + 'account_email' => $GLOBALS['auto_create_acct']['email'], + 'account_status' => 'A', + 'account_expires' => $expires, + 'account_primary_group' => $primary_group, + ); + // use given account_id, if it's not already used + if (isset($GLOBALS['auto_create_acct']['account_id']) && + is_numeric($GLOBALS['auto_create_acct']['account_id']) && + !$this->id2name($GLOBALS['auto_create_acct']['account_id'])) + { + $data['account_id'] = $GLOBALS['auto_create_acct']['account_id']; + } + if (!($data['account_id'] = $this->save($data))) + { + return false; + } + // call hook to notify interested apps about the new account + $GLOBALS['hook_values'] = $data; + $GLOBALS['egw']->hooks->process($data+array( + 'location' => 'addaccount', + // at login-time only the hooks from the following apps will be called + 'order' => array('felamimail','fudforum'), + ),False,True); // called for every app now, not only enabled ones + + return $data['account_id']; + } + + function list_methods($_type='xmlrpc') + { + if (is_array($_type)) + { + $_type = $_type['type'] ? $_type['type'] : $_type[0]; + } + + switch($_type) + { + case 'xmlrpc': + $xml_functions = array( + 'get_list' => array( + 'function' => 'get_list', + 'signature' => array(array(xmlrpcStruct)), + 'docstring' => lang('Returns a full list of accounts on the system. Warning: This is return can be quite large') + ), + 'list_methods' => array( + 'function' => 'list_methods', + 'signature' => array(array(xmlrpcStruct,xmlrpcString)), + 'docstring' => lang('Read this list of methods.') + ) + ); + return $xml_functions; + break; + case 'soap': + return $this->soap_functions; + break; + default: + return array(); + break; + } + } + + /** + * Internal functions not meant to use outside this class!!! + */ + + /** + * Sets up the account-data cache + * + * The cache is shared between all instances of the account-class and it can be save in the session, + * if use_session_cache is set to True + * + * @internal + */ + function setup_cache() + { + if ($this->use_session_cache && // are we supposed to use a session-cache + !@$GLOBALS['egw_info']['accounts']['session_cache_setup'] && // is it already setup + // is the account-class ready (startup !) + is_object($GLOBALS['egw']->session) && $GLOBALS['egw']->session->account_id) + { + // setting up the session-cache + $GLOBALS['egw_info']['accounts']['cache'] = $GLOBALS['egw']->session->appsession('accounts_cache','phpgwapi'); + $GLOBALS['egw_info']['accounts']['session_cache_setup'] = True; + //echo "accounts::setup_cache() cache=
".print_r($GLOBALS['egw_info']['accounts']['cache'],True)."
\n"; + } + if (!isset($this->cache)) + { + $this->cache = &$GLOBALS['egw_info']['accounts']['cache']; + } + if (!is_array($this->cache)) $this->cache = array(); + } + + /** + * Saves the account-data cache in the session + * + * Gets called from common::egw_final() + * + * @internal + */ + function save_session_cache() + { + if ($this->use_session_cache && // are we supposed to use a session-cache + $GLOBALS['egw_info']['accounts']['session_cache_setup'] && // is it already setup + // is the account-class ready (startup !) + is_object($GLOBALS['egw']->session)) + { + $GLOBALS['egw']->session->appsession('accounts_cache','phpgwapi',$GLOBALS['egw_info']['accounts']['cache']); + } + } + + /** + * Depricated functions of the old accounts class. + * + * Do NOT use them in new code, they will be removed after the next major release!!! + */ + + /** + * Reads the data of the account this class is instanciated for + * + * @deprecated use read of $GLOBALS['egw']->accounts and not own instances of the accounts class + * @return array with the internal data + */ + function read_repository() + { + return $this->data = $this->read($this->account_id,true); + } + + /** + * saves the account-data in the internal data-structure of this class to the repository + * + * @deprecated use save of $GLOBALS['egw']->accounts and not own instances of the accounts class + */ + function save_repository() + { + $this->save($this->data,true); + } + + /** + * Searches / lists accounts: users and/or groups + * + * @deprecated use search + */ + function get_list($_type='both',$start = '',$sort = '', $order = '', $query = '', $offset = '',$query_type='') + { + //echo "

accounts::get_list(".print_r($_type,True).",start='$start',sort='$sort',order='$order',query='$query',offset='$offset')

\n"; + $this->setup_cache(); + $account_list = &$this->cache['account_list']; + + // For XML-RPC + if (is_array($_type)) + { + $p = $_type; + $_type = $p['type']; + $start = $p['start']; + $order = $p['order']; + $query = $p['query']; + $offset = $p['offset']; + $query_type = $p['query_type']; + } + else + { + $p = array( + 'type' => $_type, + 'start' => $start, + 'order' => $order, + 'query' => $query, + 'offset' => $offset, + 'query_type' => $query_type , + ); + } + $serial = serialize($p); + + if (isset($account_list[$serial])) + { + $this->total = $account_list[$serial]['total']; + } + else + { + $account_list[$serial]['data'] = parent::get_list($_type,$start,$sort,$order,$query,$offset,$query_type); + $account_list[$serial]['total'] = $this->total; + } + return $account_list[$serial]['data']; + } + + /** + * Create a new account with the given $account_info + * + * @deprecated use save + * @param array $data account data for the new account + * @param booelan $default_prefs has no meaning any more, as we use "real" default prefs since 1.0 + * @return int new nummeric account-id + */ + function create($account_info,$default_prefs=True) + { + return $this->save($account_info); + } + + /** + * copies the given $data into the internal array $this->data + * + * @deprecated store data in your own code and use save to save it + * @param array $data array with account data + * @return array $this->data = $data + */ + function update_data($data) + { + return $this->data = $data; + } + + /** + * Get all memberships of an account $accountid / groups the account is a member off + * + * @deprecated use memberships() which account_id => account_lid pairs + * @param int/string $accountid='' numeric account-id or alphanum. account-lid, + * default account of the user of this session + * @return array or arrays with keys 'account_id' and 'account_name' for the groups $accountid is a member of + */ + function membership($accountid = '') + { + $accountid = get_account_id($accountid); + + if (!($memberships = $this->memberships($accountid))) + { + return $memberships; + } + $old = array(); + foreach($memberships as $id => $lid) + { + $old[] = array('account_id' => $id, 'account_name' => $lid); + } + //echo "

accounts::membership($accountid)="; _debug_array($old); + return $old; + } + + /** + * Get all members of the group $accountid + * + * @deprecated use members which returns acount_id => account_lid pairs + * @param int/string $accountid='' numeric account-id or alphanum. account-lid, + * default account of the user of this session + * @return array of arrays with keys 'account_id' and 'account_name' + */ + function member($accountid) + { + if (!($members = $this->members($accountid))) + { + return $members; + } + $old = array(); + foreach($members as $uid => $lid) + { + $old[] = array('account_id' => $uid, 'account_name' => $lid); + } + return $old; + } + + /** + * phpGW compatibility function, better use split_accounts + * + * @deprecated use split_accounts + */ + function return_members($accounts) + { + $arr = $this->split_accounts($accounts); + + return array( + 'users' => $arr['accounts'], + 'groups' => $arr['groups'], + ); + } + + + /** + * Gets account-name (lid), firstname and lastname of an account $accountid + * + * @deprecated use read to read account data + * @param int/string $accountid='' numeric account-id or alphanum. account-lid, + * if !$accountid account of the user of this session + * @param string &$lid on return: alphanumeric account-name (lid) + * @param string &$fname on return: first name + * @param string &$lname on return: last name + * @return boolean true if $accountid was found, false otherwise + */ + function get_account_name($accountid,&$lid,&$fname,&$lname) + { + if (!($data = $this->read($accountid))) return false; + + $lid = $data['account_lid']; + $fname = $data['account_firstname']; + $lname = $data['account_lastname']; + + if (empty($fname)) $fname = $lid; + if (empty($lname)) $lname = $this->get_type($accountid) == 'g' ? lang('Group') : lang('user'); + + return true; + } + + /** + * Reads account-data for a given $account_id from the repository AND sets the class-vars with it + * + * Same effect as instanciating the class with that account, dont do it with $GLOBALS['egw']->account !!! + * + * @deprecated use read to read account data and store it in your own code + * @param int $accountid numeric account-id + * @return array with keys lid, firstname, lastname, fullname, type + */ + function get_account_data($account_id) + { + $this->account_id = $account_id; + $this->read_repository(); + + $data[$this->data['account_id']]['lid'] = $this->data['account_lid']; + $data[$this->data['account_id']]['firstname'] = $this->data['firstname']; + $data[$this->data['account_id']]['lastname'] = $this->data['lastname']; + $data[$this->data['account_id']]['fullname'] = $this->data['fullname']; + $data[$this->data['account_id']]['type'] = $this->data['account_type']; + + return $data; + } +} + +/** + * Enable this only, if your system users are automatically eGroupWare users. + * This is NOT the case for most installations and silently rejecting all this names causes a lot of trouble. + +$GLOBALS['egw_info']['server']['global_denied_users'] = array( + 'root' => True, 'bin' => True, 'daemon' => True, + 'adm' => True, 'lp' => True, 'sync' => True, + 'shutdown' => True, 'halt' => True, 'ldap' => True, + 'mail' => True, 'news' => True, 'uucp' => True, + 'operator' => True, 'games' => True, 'gopher' => True, + 'nobody' => True, 'xfs' => True, 'pgsql' => True, + 'mysql' => True, 'postgres' => True, 'oracle' => True, + 'ftp' => True, 'gdm' => True, 'named' => True, + 'alias' => True, 'web' => True, 'sweep' => True, + 'cvs' => True, 'qmaild' => True, 'qmaill' => True, + 'qmaillog' => True, 'qmailp' => True, 'qmailq' => True, + 'qmailr' => True, 'qmails' => True, 'rpc' => True, + 'rpcuser' => True, 'amanda' => True, 'apache' => True, + 'pvm' => True, 'squid' => True, 'ident' => True, + 'nscd' => True, 'mailnull' => True, 'cyrus' => True, + 'backup' => True +); + +$GLOBALS['egw_info']['server']['global_denied_groups'] = array( + 'root' => True, 'bin' => True, 'daemon' => True, + 'sys' => True, 'adm' => True, 'tty' => True, + 'disk' => True, 'lp' => True, 'mem' => True, + 'kmem' => True, 'wheel' => True, 'mail' => True, + 'uucp' => True, 'man' => True, 'games' => True, + 'dip' => True, 'ftp' => True, 'nobody' => True, + 'floppy' => True, 'xfs' => True, 'console' => True, + 'utmp' => True, 'pppusers' => True, 'popusers' => True, + 'slipusers' => True, 'slocate' => True, 'mysql' => True, + 'dnstools' => True, 'web' => True, 'named' => True, + 'dba' => True, 'oinstall' => True, 'oracle' => True, + 'gdm' => True, 'sweep' => True, 'cvs' => True, + 'postgres' => True, 'qmail' => True, 'nofiles' => True, + 'ldap' => True, 'backup' => True +); +*/ \ No newline at end of file diff --git a/phpgwapi/inc/class.accounts_contacts.inc.php b/phpgwapi/inc/class.accounts_contacts.inc.php deleted file mode 100644 index 1667f4ebde..0000000000 --- a/phpgwapi/inc/class.accounts_contacts.inc.php +++ /dev/null @@ -1,345 +0,0 @@ - * - * View and manipulate account records using the contacts class * - * Copyright (C) 2000, 2001 Miles Lott * - * -------------------------------------------------------------------------* - * This library is part of the eGroupWare API * - * http://www.egroupware.org/api * - * ------------------------------------------------------------------------ * - * This library is free software; you can redistribute it and/or modify it * - * under the terms of the GNU Lesser General Public License as published by * - * the Free Software Foundation; either version 2.1 of the License, * - * or any later version. * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * See the GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License * - * along with this library; if not, write to the Free Software Foundation, * - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - \**************************************************************************/ - - /* $Id$ */ - - - /* - THIS NEEDS WORK!!!!!!!!! - Milosch - But it is a lot closer now... - */ - $GLOBALS['egw_info']['server']['global_denied_users'] = array( - 'root' => True, 'bin' => True, 'daemon' => True, - 'adm' => True, 'lp' => True, 'sync' => True, - 'shutdown' => True, 'halt' => True, 'ldap' => True, - 'mail' => True, 'news' => True, 'uucp' => True, - 'operator' => True, 'games' => True, 'gopher' => True, - 'nobody' => True, 'xfs' => True, 'pgsql' => True, - 'mysql' => True, 'postgres' => True, 'oracle' => True, - 'ftp' => True, 'gdm' => True, 'named' => True, - 'alias' => True, 'web' => True, 'sweep' => True, - 'cvs' => True, 'qmaild' => True, 'qmaill' => True, - 'qmaillog' => True, 'qmailp' => True, 'qmailq' => True, - 'qmailr' => True, 'qmails' => True, 'rpc' => True, - 'rpcuser' => True, 'amanda' => True, 'apache' => True, - 'pvm' => True, 'squid' => True, 'ident' => True, - 'nscd' => True, 'mailnull' => True, 'cyrus' => True, - 'backup' => True - ); - - $GLOBALS['egw_info']['server']['global_denied_groups'] = array( - 'root' => True, 'bin' => True, 'daemon' => True, - 'sys' => True, 'adm' => True, 'tty' => True, - 'disk' => True, 'lp' => True, 'mem' => True, - 'kmem' => True, 'wheel' => True, 'mail' => True, - 'uucp' => True, 'man' => True, 'games' => True, - 'dip' => True, 'ftp' => True, 'nobody' => True, - 'floppy' => True, 'xfs' => True, 'console' => True, - 'utmp' => True, 'pppusers' => True, 'popusers' => True, - 'slipusers' => True, 'slocate' => True, 'mysql' => True, - 'dnstools' => True, 'web' => True, 'named' => True, - 'dba' => True, 'oinstall' => True, 'oracle' => True, - 'gdm' => True, 'sweep' => True, 'cvs' => True, - 'postgres' => True, 'qmail' => True, 'nofiles' => True, - 'ldap' => True, 'backup' => True - ); - - class accounts_ - { - var $db; - var $contacts; - var $account_id; - var $data; - var $debug = False; - var $qcols = array( - 'fn' => 'fn', - 'n_given' => 'n_given', - 'n_family' => 'n_family', - 'account_lastlogin' => 'account_lastlogin', - 'account_lastloginfrom' => 'account_lastloginfrom', - 'account_lastpwd_change' => 'account_lastpwd_change', - 'account_status' => 'account_status', - 'account_expires' => 'account_expires' - ); - - function accounts_() - { - $this->db = $GLOBALS['egw']->db; - $this->contacts =& CreateObject('phpgwapi.contacts',0); - } - - function makeobj() - { - if(!$this->contacts) - { - $this->contacts =& CreateObject('phpgwapi.contacts','0'); - } - } - - function read_repository() - { - $this->makeobj(); - - $allValues = $this->contacts->read_single_entry($this->account_id,$this->qcols); - - /* Now dump it into the array */ - $this->data['userid'] = $allValues[0]['lid']; - $this->data['account_id'] = $allValues[0]['id']; - $this->data['account_lid'] = $allValues[0]['lid']; - $this->data['account_type'] = $allValues[0]['tid']; - $this->data['firstname'] = $allValues[0]['n_given']; - $this->data['lastname'] = $allValues[0]['n_family']; - $this->data['fullname'] = $allValues[0]['fn']; - $this->data['lastlogin'] = $allValues[0]['account_lastlogin']; - $this->data['lastloginfrom'] = $allValues[0]['account_lastloginfrom']; - $this->data['lastpasswd_change'] = $allValues[0]['account_lastpwd_change']; - $this->data['status'] = $allValues[0]['account_status']; - $this->data['expires'] = $allValues[0]['account_expires']; - - return $this->data; - } - - function save_repository() - { - $this->makeobj(); - - $entry['id'] = $this->data['account_id']; - $entry['lid'] = $this->data['account_lid']; - $entry['tid'] = $this->data['account_type']; - $entry['fn'] = sprintf("%s %s", $this->data['firstname'], $this->data['lastname']); - $entry['n_family'] = $this->data['lastname']; - $entry['n_given'] = $this->data['firstname']; - $entry['account_lastlogin'] = $this->data['lastlogin']; - $entry['account_lastloginfrom'] = $this->data['lastloginfrom']; - $entry['account_lastpasswd_change'] = $this->data['lastpwd_change']; - $entry['account_status'] = $this->data['status']; - $entry['account_expires'] = $this->data['expires']; - - if($this->debug) { echo '
Updating entry:
' . var_dump($entry); } - $this->contacts->update($entry['id'],0,$entry,'public','',$entry['tid']); - } - - function add($account_name, $account_type, $first_name, $last_name, $passwd = False) - { - $this->create($account_name, $account_type, $first_name, $last_name, $passwd); - } - - function delete($accountid = '') - { - $this->makeobj(); - - if($this->debug) { echo '
Deleting entry:
' . $account_id; } - $account_id = get_account_id($accountid); - $this->contacts->delete($account_id); - } - - function get_list($_type='both') - { - $this->makeobj(); - - switch($_type) - { - case 'accounts': - $filter = 'tid=u'; - break; - case 'groups': - $filter = 'tid=g'; - break; - default: - $filter = 'tid=u,tid=g'; - } - - $allValues = $this->contacts->read(0,0,$this->qcols,'',$filter); - - /* get user information for each user/group */ - for($i=0;$i $allValues[$i]['id'], - 'account_lid' => $allValues[$i]['lid'], - 'account_type' => $allValues[$i]['tid'], - 'account_firstname' => $allValues[$i]['n_given'], - 'account_lastname' => $allValues[$i]['n_family'], - 'account_status' => $allValues[$i]['account_status'], - 'account_expires' => $allValues[$i]['account_expires'] - ); - } - - return $accounts; - } - - function name2id($account_lid) - { - $qcols = array('id' => 'id'); - $this->makeobj(); - $allValues = $this->contacts->read(0,0,$qcols,'',"lid=".$account_lid); - - if($allValues[0]['id']) - { - return (int)$allValues[0]['id']; - } - else - { - return False; - } - } - - function id2name($account_id) - { - $this->makeobj(); - - $allValues = $this->contacts->read_single_entry($account_id); - if($this->debug) { echo '
id2name: '.$allValues[0]['lid']; } - - if($allValues[0]['lid']) - { - return $allValues[0]['lid']; - } - else - { - return False; - } - } - - function get_type($accountid = '') - { - $this->makeobj(); - $account_id = get_account_id($accountid); - - $allValues = $this->contacts->read_single_entry($account_id); - - if ($allValues[0]['tid']) - { - return $allValues[0]['tid']; - } - else - { - return False; - } - } - - function exists($account_lid) - { - $this->makeobj(); - if(is_int($account_lid)) - { - $account_id = $account_lid; - settype($account_lid,'string'); - $account_lid = $this->id2name($account_id); - } - - $allValues = $this->contacts->read(0,0,array('n_given' => 'n_given'),'','lid='.$account_lid); - - if ($allValues[0]['id']) - { - return True; - } - else - { - return False; - } - } - - function create($account_info) - { - $this->makeobj(); - - if (!$$account_info['account_id']) - { - $account_info['account_id'] = $this->get_nextid(); - } - $owner = $GLOBALS['egw_info']['user']['account_id']; - $entry['id'] = $account_info['account_id']; - $entry['lid'] = $account_info['account_lid']; - $entry['n_given'] = $account_info['account_firstname']; - $entry['n_family'] = $account_info['account_lastname']; - $entry['password'] = $account_info['account_passwd']; - $entry['account_status'] = $account_info['account_status']; - $entry['account_expires'] = $account_info['account_expires']; - - if($this->debug) { echo '
Adding entry:
' . var_dump($entry); } - /* 'public' access, no category id, tid set to account_type */ - $this->contacts->add(0,$entry,'public','',$account_info['account_type']); - return; - } - - function auto_add($accountname, $passwd, $default_prefs = False, $default_acls = False, $expiredate = 0, $account_status = 'A') - { - if (! $expiredate) - { - // expire in 30 days by default - $expiredate = time() + ( ( 60 * 60 ) * (30 * 24) ); - } - - $default_group_id = $this->name2id($GLOBALS['egw_info']['server']['default_group_lid']); - if (!$default_group_id) - { - $default_group_id = (int) $this->name2id('Default'); - } - $primary_group = $GLOBALS['auto_create_acct']['primary_group'] && - $this->get_type((int)$GLOBALS['auto_create_acct']['primary_group']) == 'g' ? - (int) $GLOBALS['auto_create_acct']['primary_group'] : $default_group_id; - - $acct_info = array( - 'account_lid' => $accountname, - 'account_type' => 'u', - 'account_passwd' => $passwd, - 'account_firstname' => $GLOBALS['auto_create_acct']['firstname'] ? $GLOBALS['auto_create_acct']['firstname'] : 'New', - 'account_lastname' => $GLOBALS['auto_create_acct']['lastname'] ? $GLOBALS['auto_create_acct']['lastname'] : 'User', - 'account_status' => $account_status, - 'account_expires' => mktime(2,0,0,date('n',$expiredate), (int)date('d',$expiredate), date('Y',$expiredate)), - 'account_primary_group' => $primary_group, - ); - if (isset($GLOBALS['auto_create_acct']['email']) == True && $GLOBALS['auto_create_acct']['email'] != '') - { - $acct_info['account_email'] = $GLOBALS['auto_create_acct']['email']; - } - elseif(isset($GLOBALS['egw_info']['server']['mail_suffix']) == True && $GLOBALS['egw_info']['server']['mail_suffix'] != '') - { - $acct_info['account_email'] = $accountname . '@' . $GLOBALS['egw_info']['server']['mail_suffix']; - } - - $this->create($acct_info); - $accountid = $this->name2id($accountname); - - if ($accountid) - { - /* If we have a primary_group, add it as "regular" eGW group (via ACL) too. */ - if ($primary_group) - { - $GLOBALS['egw']->acl->add_repository('phpgw_group', $primary_group,$accountid,1); - } - // call hook to notify other apps about the new account - $GLOBALS['hook_values']['account_lid'] = $acct_info['account_lid']; - $GLOBALS['hook_values']['account_id'] = $accountid; - $GLOBALS['hook_values']['new_passwd'] = $acct_info['account_passwd']; - $GLOBALS['hook_values']['account_status'] = $acct_info['account_status']; - $GLOBALS['hook_values']['account_firstname'] = $acct_info['account_firstname']; - $GLOBALS['hook_values']['account_lastname'] = $acct_info['account_lastname']; - $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( - 'location' => 'addaccount' - ),False,True); /* called for every app now, not only enabled ones */ - } - return $accountid; - } - } diff --git a/phpgwapi/inc/class.accounts_ldap.inc.php b/phpgwapi/inc/class.accounts_ldap.inc.php index a7fe853ac3..6a0d918f6a 100644 --- a/phpgwapi/inc/class.accounts_ldap.inc.php +++ b/phpgwapi/inc/class.accounts_ldap.inc.php @@ -1,1124 +1,786 @@ * - * and Lars Kneschke * - * and Miles Lott * - * and Bettina Gille * - * View and manipulate account records using LDAP * - * Copyright (C) 2000 - 2002 Joseph Engo, Lars Kneschke * - * Copyright (C) 2003 Lars Kneschke, Bettina Gille * - * ------------------------------------------------------------------------ * - * This library is part of the eGroupWare API * - * http://www.egroupware.org * - * ------------------------------------------------------------------------ * - * This library is free software; you can redistribute it and/or modify it * - * under the terms of the GNU Lesser General Public License as published by * - * the Free Software Foundation; either version 2.1 of the License, * - * or any later version. * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * See the GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License * - * along with this library; if not, write to the Free Software Foundation, * - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - \**************************************************************************/ - /* $Id$ */ +/** + * API - accounts LDAP backend + * + * The LDAP backend of the accounts class now stores accounts, groups and the memberships completly in LDAP. + * It does NO longer use the ACL class/table for group membership information. + * Nor does it use the phpgwAcounts schema (part of that information is stored via shadowAccount now). + * + * A user is recogniced by eGW, if he's in the user_context tree AND has the posixAccount object class. + * A group is recogniced by eGW, if it's in the group_context tree AND has the posixGroup object class. + * The group members are stored as memberuid's. + * + * The (positive) group-id's (gidnumber) of LDAP groups are mapped in this class to negative numeric + * account_id's to not conflict with the user-id's, as both share in eGW internaly the same numberspace! + * + * @link http://www.egroupware.org + * @author Ralf Becker complete rewrite in 6/2006 + * + * This class replaces the former accounts_ldap class written by + * Joseph Engo , Lars Kneschke , + * Miles Lott and Bettina Gille . + * Copyright (C) 2000 - 2002 Joseph Engo, Lars Kneschke + * Copyright (C) 2003 Lars Kneschke, Bettina Gille + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @subpackage accounts + * @version $Id$ + */ - class accounts_ +/** + * LDAP Backend for accounts + * + * @author Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @subpackage accounts + * @access internal only use the interface provided by the accounts class + */ +class accounts_backend +{ + /** + * resource with connection to the ldap server + * + * @var resource + */ + var $ds; + /** + * LDAP context for users, eg. ou=account,dc=domain,dc=com + * + * @var string + */ + var $user_context; + /** + * LDAP context for groups, eg. ou=groups,dc=domain,dc=com + * + * @var string + */ + var $group_context; + /** + * total number of found entries from get_list method + * + * @var int + */ + var $total; + + /** + * required classe for user and groups + * + * @var array + */ + var $requiredObjectClasses = array( + 'user' => array( + 'top','person','organizationalperson','inetorgperson','posixaccount','shadowaccount' + ), + 'group' => array( + 'top','posixgroup', + // some newer ldap require namedObject here, as none of the above is a structural object there + // this gets now autodetected + //'namedObject' + ) + ); + /** + * reference to the translation class + * + * @var object + */ + var $translation; + + /** + * Constructor + * + * @return accounts_backend + */ + function accounts_backend() { - var $db; - var $ds; - var $account_id; - var $data; - var $user_context = ''; - var $group_context = ''; - var $total; + // enable the caching in the session, done by the accounts class extending this class. + $this->use_session_cache = true; - var $requiredObjectClasses = array( - 'user' => array( - 'top','person','organizationalperson','inetorgperson','posixaccount','shadowaccount','phpgwaccount' - ), - 'group' => array( - 'top','posixgroup','phpgwaccount', - // some newer ldap require namedObject here, as none of the above is a structural object there - // this gets now autodetected - //'namedObject' - ) - ); - - function accounts_() + $this->ds = $GLOBALS['egw']->common->ldapConnect(); + if(!@is_object($GLOBALS['egw']->translation)) { - $this->ds = $GLOBALS['egw']->common->ldapConnect(); - if(!@is_object($GLOBALS['egw']->translation)) - { - $GLOBALS['egw']->translation =& CreateObject('phpgwapi.translation'); - } - $this->user_context = $GLOBALS['egw_info']['server']['ldap_context']; - $this->group_context = $GLOBALS['egw_info']['server']['ldap_group_context'] ? - $GLOBALS['egw_info']['server']['ldap_group_context'] : $GLOBALS['egw_info']['server']['ldap_context']; + $GLOBALS['egw']->translation =& CreateObject('phpgwapi.translation'); } + $this->translation =& $GLOBALS['egw']->translation; - function read_repository() + $this->user_context = $GLOBALS['egw_info']['server']['ldap_context']; + $this->group_context = $GLOBALS['egw_info']['server']['ldap_group_context'] ? + $GLOBALS['egw_info']['server']['ldap_group_context'] : $GLOBALS['egw_info']['server']['ldap_context']; + } + + /** + * Reads the data of one account + * + * @param int $account_id numeric account-id + * @return array/boolean array with account data (keys: account_id, account_lid, ...) or false if account not found + */ + function read($account_id) + { + if (!(int)$account_id) return false; + + if ($account_id < 0) { - $acct_type = $this->get_type($this->account_id); - - /* search the dn for the given uid */ - if(($acct_type == 'g') && $this->group_context) - { - $sri = ldap_search($this->ds, $this->group_context, 'gidnumber=' . abs((int)$this->account_id)); - } - else - { - $sri = ldap_search($this->ds, $this->user_context, 'uidnumber=' . (int)$this->account_id); - } - $allValues = ldap_get_entries($this->ds, $sri); - - /* Now dump it into the array; take first entry found */ - if($acct_type =='g') - { - $this->data['account_id'] = -$allValues[0]['gidnumber'][0]; - $this->data['account_lid'] = $allValues[0]['cn'][0]; - $this->data['firstname'] = $GLOBALS['egw']->translation->convert($allValues[0]['cn'][0],'utf-8'); - $this->data['lastname'] = lang('Group'); - } - else - { - $this->data['account_id'] = $allValues[0]['uidnumber'][0]; - $this->data['account_primary_group'] = -$allValues[0]['gidnumber'][0]; - $this->data['account_lid'] = $allValues[0]['uid'][0]; - $this->data['firstname'] = $GLOBALS['egw']->translation->convert($allValues[0]['givenname'][0],'utf-8'); - $this->data['lastname'] = $GLOBALS['egw']->translation->convert($allValues[0]['sn'][0],'utf-8'); - if(isset($allValues[0]['mail'][0])) - { - $this->data['email'] = $allValues[0]['mail'][0]; - } - } - $this->data['account_dn'] = $allValues[0]['dn']; - $this->data['fullname'] = $GLOBALS['egw']->translation->convert($allValues[0]['cn'][0],'utf-8'); - - if ($GLOBALS['egw_info']['server']['ldap_extra_attributes']) - { - $this->data['homedirectory'] = $allValues[0]['homedirectory'][0]; - $this->data['loginshell'] = $allValues[0]['loginshell'][0]; - } - - $this->data['lastlogin'] = $allValues[0]['phpgwaccountlastlogin'][0]; - $this->data['lastloginfrom'] = $allValues[0]['phpgwaccountlastloginfrom'][0]; - $this->data['lastpasswd_change'] = @$allValues[0]['phpgwlastpasswdchange'][0]; - $this->data['status'] = trim($allValues[0]['phpgwaccountstatus'][0]); - $this->data['type'] = $allValues[0]['phpgwaccounttype'][0]; - $this->data['expires'] = $allValues[0]['phpgwaccountexpires'][0]; - - return $this->data; + return $this->_read_group($account_id); } - - function save_repository() - { - $acct_type = $this->get_type($this->account_id); - - /* search the dn for the given u/gidnumber */ - if(($acct_type == 'g') && $this->group_context) - { - $sri = ldap_search($this->ds, $this->group_context, 'gidnumber=' . abs((int)$this->account_id)); - } - else - { - $sri = ldap_search($this->ds, $this->user_context, 'uidnumber=' . (int)$this->account_id); - } - $allValues = ldap_get_entries($this->ds, $sri); - - $this->data['account_type'] = $allValues[0]['phpgwaccounttype'][0]; - - if($acct_type == 'u') - { - // data for posixaccount - $newData['cn'] = $GLOBALS['egw']->translation->convert(sprintf("%s %s", - $this->data['firstname'], - $this->data['lastname']),$GLOBALS['egw']->translation->charset(),'utf-8' - ); - $newData['uid'] = $GLOBALS['egw']->translation->convert( - $this->data['account_lid'], - $GLOBALS['egw']->translation->charset(),'utf-8' - ); - if($this->data['lastname']) - { - $newData['sn'] = $GLOBALS['egw']->translation->convert( - $this->data['lastname'], - $GLOBALS['egw']->translation->charset(),'utf-8' - ); - } - - if($this->data['firstname']) - { - $newData['givenname'] = $GLOBALS['egw']->translation->convert( - $this->data['firstname'], - $GLOBALS['egw']->translation->charset(),'utf-8' - ); - } - if ($GLOBALS['egw_info']['server']['ldap_extra_attributes']) - { - $newData['homedirectory'] = $this->data['homedirectory']; - $newData['loginshell'] = $this->data['loginshell']; - } - else - { - // the posixaccount schema requires this - $entry['homedirectory'] = '/home/'.$this->data['account_lid']; - $entry['loginshell'] = '/bin/false'; - } - if($this->data['account_primary_group']) - { - $newData['gidnumber'] = abs($this->data['account_primary_group']); - } - if($this->data['lastlogin']) - { - $newData['phpgwaccountlastlogin'] = $this->data['lastlogin']; - } - if($this->data['lastloginfrom']) - { - $newData['phpgwaccountlastloginfrom'] = $this->data['lastloginfrom']; - } - if($this->data['lastpasswd_change']) - { - $newData['phpgwlastpasswdchange'] = $this->data['lastpasswd_change']; - } - if($this->data['status']) - { - $newData['phpgwaccountstatus'] = $this->data['status']; - } - else - { - $newData['phpgwaccountstatus'] = array(); - } - if($this->data['expires']) - { - $newData['phpgwaccountexpires'] = $this->data['expires']; - } - if($this->data['email']) - { - $newData['mail'] = $this->data['email']; - } - - // check that we have all required objectclasses - unset($allValues[0]['objectclass']['count']); - // convert all values to lowercase - $currentObjectClasses = array_flip(array_change_key_case(array_flip($allValues[0]['objectclass']))); - $missingObjectClasses = array_diff($this->requiredObjectClasses['user'],$currentObjectClasses); - if(count($missingObjectClasses) > 0) - { - $newData['objectclass'] = array_merge($currentObjectClasses, $missingObjectClasses); - } - $newAccountID = $newData['uid']; - $oldAccountID = $newData['uid']; - } - else - { - // data for posixgroup - $newData['cn'] = $GLOBALS['egw']->translation->convert( - $this->data['account_lid'], - $GLOBALS['egw']->translation->charset(), 'utf-8' - ); - $newData['gidnumber'] = abs($this->account_id); - $newGroupID = $newData['cn']; - $oldGroupID = $newData['cn']; - } - if($this->data['account_type']) - { - $newData['phpgwaccounttype'] = $this->data['account_type']; - } - - /* - Changing the uid: Need to delete and add new, since - PHP cannot change the dn for the entry. - */ - if ($acct_type == 'g') - { - $test = $allValues[0]['cn'][0]; - } - else - { - $test = $allValues[0]['uid'][0]; - } - if($GLOBALS['egw']->translation->convert($test,'utf-8') != $this->data['account_lid']) - { - $oldData = $allValues[0]; - $oldDN = $oldData['dn']; - // remove all unneeded fields - unset($oldData['dn']); - unset($oldData['count']); - foreach($oldData as $key => $value) - { - if(is_numeric($key)) - { - // remove the key, its no ldap key - unset($oldData[$key]); - } - else - { - // remove the count key - if($oldData[$key]['count'] == 1) - { - $oldData[$key] = $value[0]; - } - else - { - unset($oldData[$key]['count']); - } - } - } - - $oldAccountID = $oldData['uid']; - $oldGroupID = $oldData['cn']; - - // merge the old data with the new one - $newData = array_merge($oldData, $newData); - - /* Groups */ - if($this->data['account_type'] == 'g' && $this->group_context ) - { - $newDN = 'cn='.$this->data['account_lid'].','.$this->group_context; - $newData['memberuid'] = array(); - if (($members = $this->member($this->account_id))) - { - foreach($members as $member) - { - if (!in_array($member['account_name'],$newData['memberuid'])) - { - $newData['memberuid'][] = $member['account_name']; - } - } - } - } - /* Accounts */ - else - { - $newDN = 'uid='.$this->data['account_lid'].','.$this->user_context; - } - // delete the old account - ldap_delete($this->ds,$oldDN); - - // add the new account - #_debug_array($newData); - if (!@ldap_add($this->ds, $newDN, $newData) && $this->data['account_type'] == 'g') - { - // try again with namedObject added, in case we have the newer schema which eg. SuSE uses - // in which our required objectclasses for a group, have no structural object - $newData['objectclass'][] = 'namedObject'; - ldap_add($this->ds, $newDN, $newData); - } - } - /* Normal behavior for save_repository update Account */ - else - { - // add the list group members - if($this->data['account_type'] == 'g' && ($members = $this->member($this->account_id))) - { - $newData['memberuid'] = array(); - foreach($members as $member) - { - if (!in_array($member['account_name'],$newData['memberuid'])) - { - $newData['memberuid'][] = $member['account_name']; - } - } - } - // modify the DN - //echo "

ldap_modify(,'{$allValues[0]['dn']}',".print_r($newData,true).")

\n"; - ldap_modify($this->ds, $allValues[0]['dn'], $newData); - } - - if ($this->data['account_type'] == 'u') - { - // lets check for groups, the user needs to be removed - - // first lets search for the groups, the user is currently member of - // and from which he needs to be removed - $filter = "(&(objectclass=posixgroup)(memberuid=" . (int)$oldAccountID . "))"; - $justThese = array('memberuid','gidnumber'); - $sri = ldap_search($this->ds, $this->group_context, $filter, $justThese); - if($sri) - { - $allValues = ldap_get_entries($this->ds, $sri); - if($allValues['count'] > 0) - { - unset($allValues['count']); - foreach($allValues as $key) - { - #_debug_array($key); - #_debug_array($this->data['account_groups']); - // delete the old accountid from any group - if($newAccountID != $oldAccountID) - { - $dn = $key['dn']; - $newData = array(); - $newData['memberuid'] = $key['memberuid']; - unset($newData['memberuid']['count']); - // remove the uid from memberuid - $newData['memberuid'] = array_flip($newData['memberuid']); - unset($newData['memberuid'][$oldAccountID]); - # $newData['memberuid'] = array_values(sort(array_flip($newData['memberuid']))); - $newData['memberuid'] = array_values(array_flip($newData['memberuid'])); - ldap_mod_replace($this->ds, $dn, $newData); - #print ldap_error($this->ds); - } - else - { - if(!in_array($key['gidnumber'][0],$this->data['account_groups'])) - { - $dn = $key['dn']; - $newData = array(); - $newData['memberuid'] = $key['memberuid']; - unset($newData['memberuid']['count']); - // remove the uid from memberuid - $newData['memberuid'] = array_flip($newData['memberuid']); - unset($newData['memberuid'][$oldAccountID]); - $newData['memberuid'] = array_values(sort(array_flip($newData['memberuid']))); - ldap_mod_replace($this->ds, $dn, $newData); - #print ldap_error($this->ds); - } - } - } - } - } - - // lets check group the user needs to be added - foreach($this->data['account_groups'] as $key => $value) - { - // search for the group - $filter = 'gidnumber=' . abs((int)$value); - $justThese = array('memberuid'); - $sri = ldap_search($this->ds, $this->group_context, $filter, $justThese); - if($sri) - { - $allValues = ldap_get_entries($this->ds, $sri); - // if the user is not member of this group, add him - if(is_array($allValues[0]['memberuid'])) - { - // this group has already some members - if(!in_array($newData['uid'],$allValues[0]['memberuid'])) - { - $dn = $allValues[0]['dn']; - $newData = array(); - $newData['memberuid'] = $allValues[0]['memberuid']; - unset($newData['memberuid']['count']); - $newData['memberuid'][] = $newAccountID; - $newData['memberuid'] = array_values(array_unique($newData['memberuid'])); - ldap_mod_replace($this->ds, $dn, $newData); - } - } - else - { - // this group has no members - $dn = $allValues[0]['dn']; - $newData = array(); - $newData['memberuid'][] = $newAccountID; - ldap_mod_replace($this->ds, $dn, $newData); - } - } - } - } - } - - function delete($accountid = '') - { - $account_id = get_account_id($accountid); - $account_lid = $this->id2name((int)$account_id); - - if ($account_id < 0) - { - $filter = 'gidnumber=' . abs((int)$account_id); - $context = $this->group_context; - } - else - { - $filter = 'uid=' . (string)$account_lid; - $context = $this->user_context; - $wasAccount = True; - } - - $sri = ldap_search($this->ds, $context, $filter); - if($sri) - { - $allValues = ldap_get_entries($this->ds, $sri); - $accountID = $allValues['0']['uid'][0]; - } - - if ($allValues[0]['dn']) - { - $del = ldap_delete($this->ds, $allValues[0]['dn']); - } - - if($wasAccount) - { - // remove the user from any group he is member of - $filter = "(&(objectclass=posixgroup)(memberuid=" . $accountID . "))"; - $justThese = array('memberuid','gidnumber'); - $sri = ldap_search($this->ds, $this->group_context, $filter, $justThese); - if($sri) - { - $allValues = ldap_get_entries($this->ds, $sri); - if($allValues['count'] > 0) - { - unset($allValues['count']); - foreach($allValues as $key) - { - $dn = $key['dn']; - $newData = array(); - $newData['memberuid'] = $key['memberuid']; - unset($newData['memberuid']['count']); - // remove the uid from memberuid - $newData['memberuid'] = array_flip($newData['memberuid']); - unset($newData['memberuid'][$accountID]); - $newData['memberuid'] = array_unique(array_flip($newData['memberuid'])); - ldap_mod_replace($this->ds, $dn, $newData); - } - } - } - } - } - - function get_list($_type='both', $start = '',$sort = '', $order = '', $query = '', $offset = '', $query_type='') - { - //print "\$_type=$_type, \$start=$start , \$sort=$sort, \$order=$order, \$query=$query, \$offset=$offset, \$query_type=$query_type
"; - $query = strtolower($query); - - if($_type != 'groups') - { - $filter = "(&(uidnumber=*)(phpgwaccounttype=u)"; - if (!empty($query) && $query != '*') - { - switch($query_type) - { - case 'all': - default: - $query = '*'.$query; - // fall-through - case 'start': - $query .= '*'; - // fall-through - case 'exact': - $filter .= "(|(uid=$query)(sn=$query)(cn=$query)(givenname=$query)(mail=$query))"; - break; - case 'firstname': - case 'lastname': - case 'lid': - case 'email': - $to_ldap = array( - 'firstname' => 'givenname', - 'lastname' => 'sn', - 'lid' => 'uid', - 'email' => 'mail', - ); - $filter .= '('.$to_ldap[$query_type].'=*'.$query.'*)'; - break; - } - } - $filter .= ')'; - - $sri = ldap_search($this->ds, $this->user_context, $filter); - $allValues = ldap_get_entries($this->ds, $sri); - while (list($null,$allVals) = @each($allValues)) - { - settype($allVals,'array'); - $test = @$allVals['uid'][0]; - if (!$GLOBALS['egw_info']['server']['global_denied_users'][$test] && $allVals['uid'][0]) - { - $accounts[] = Array( - 'account_id' => $allVals['uidnumber'][0], - 'account_lid' => $allVals['uid'][0], - 'account_type' => $allVals['phpgwaccounttype'][0], - 'account_firstname' => $GLOBALS['egw']->translation->convert($allVals['givenname'][0],'utf-8'), - 'account_lastname' => $GLOBALS['egw']->translation->convert($allVals['sn'][0],'utf-8'), - 'account_status' => $allVals['phpgwaccountstatus'][0], - 'account_email' => $allVals['mail'][0], - ); - } - } - } - if ($_type != 'accounts') - { - if(empty($query) || $query == '*') - { - $filter = '(&(gidnumber=*)(phpgwaccounttype=g))'; - } - else - { - $filter = "(&(gidnumber=*)(phpgwaccounttype=g)(|(uid=*$query*)(sn=*$query*)(cn=*$query*)(givenname=*$query*)))"; - } - $sri = ldap_search($this->ds, $this->group_context, $filter); - $allValues = ldap_get_entries($this->ds, $sri); - while (list($null,$allVals) = @each($allValues)) - { - settype($allVals,'array'); - $test = $allVals['cn'][0]; - if (!$GLOBALS['egw_info']['server']['global_denied_groups'][$test] && $allVals['cn'][0]) - { - $accounts[] = Array( - 'account_id' => -$allVals['gidnumber'][0], - 'account_lid' => $allVals['cn'][0], - 'account_type' => $allVals['phpgwaccounttype'][0], - 'account_firstname' => $GLOBALS['egw']->translation->convert($allVals['givenname'][0],'utf-8'), - 'account_lastname' => $GLOBALS['egw']->translation->convert($allVals['sn'][0],'utf-8'), - 'account_status' => $allVals['phpgwaccountstatus'][0], - 'account_email' => $allVals['mail'][0], - ); - } - } - } - // sort the array - $arrayFunctions =& CreateObject('phpgwapi.arrayfunctions'); - if(empty($order)) - { - $order = 'account_lid'; - } - $sortedAccounts = $arrayFunctions->arfsort($accounts,explode(',',$order),$sort); - $this->total = count($accounts); - // return only the wanted accounts - if (is_array($sortedAccounts)) - { - reset($sortedAccounts); - if(is_numeric($start) && is_numeric($offset)) - { - return array_slice($sortedAccounts, $start, $offset); - } - elseif(is_numeric($start)) - { - return array_slice($sortedAccounts, $start, $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']); - } - else - { - return $sortedAccounts; - } - } - return False; - } - - function name2id($name,$which='account_lid') - { - if ($which == 'account_lid') // groups only support account_lid - { - $sri = ldap_search($this->ds, $this->group_context, '(&(cn=' . (string)$name . ')(phpgwaccounttype=g))'); - $allValues = ldap_get_entries($this->ds, $sri); - - if (@$allValues[0]['gidnumber'][0]) - { - return -(int)$allValues[0]['gidnumber'][0]; - } - } - $to_ldap = array( - 'account_lid' => 'uid', - 'account_email' => 'mail', - ); - if (!isset($to_ldap[$which])) return False; - - $sri = ldap_search($this->ds, $this->user_context, '(&('.$to_ldap[$which].'=' . (string)$name . ')(phpgwaccounttype=u))'); - - $allValues = ldap_get_entries($this->ds, $sri); - - if (@$allValues[0]['uidnumber'][0]) - { - return (int)$allValues[0]['uidnumber'][0]; - } - - return False; - } - - function id2name($account_id,$which='account_lid') - { - if (($which == 'account_lid' || $which == 'account_type') && $account_id < 0) // groups only support account_lid and account_type - { - $allValues = array(); - $sri = ldap_search($this->ds, $this->group_context, '(&(gidnumber=' . abs((int)$account_id) . ')(phpgwaccounttype=g))'); - $allValues = ldap_get_entries($this->ds, $sri); - - $attr = $which == 'account_lid' ? 'cn' : 'phpgwaccounttype'; - if (@$allValues[0]['cn'][0]) - { - return $allValues[0]['cn'][0]; - } - } - $to_ldap = array( - 'account_lid' => 'uid', - 'account_email' => 'mail', - 'account_firstname' => 'surname', - 'account_lastname' => 'cn', - 'account_type' => 'phpgwaccounttype', - ); - if (!isset($to_ldap[$which])) return False; - - $allValues = array(); - $sri = ldap_search($this->ds, $this->user_context, '(&(uidnumber=' . (int)$account_id . ')(phpgwaccounttype=u))'); - $allValues = ldap_get_entries($this->ds, $sri); - - if (@$allValues[0][$to_ldap[$which]][0]) - { - return $allValues[0][$to_ldap[$which]][0]; - } - return False; - } - - /* - * returns nonzero if $account exists in LDAP: 0: nowhere 1: user accounts, 2: group accounts, 3: both - * $account can be an account_id (LDAP: uidnumber) or an account_lid (LDAP: uid) (is determinded by ettype($account) == 'integer') - */ - function exists($account) - { - /* This sets up internal caching variables for this functon */ - static $by_id, $by_lid; - $users = array(); - $groups = array(); - - if(is_numeric($account)) - { - $ldapgroup = 'gidnumber'; - $ldapacct = 'uidnumber'; - /* If data is cached, use it. */ - if(isset($by_id[$account])) - { - return $by_id[$account]; - } - } - else - { - $ldapgroup = 'cn'; - $ldapacct = 'uid'; - /* If data is cached, use it. */ - if(@isset($by_lid[$account]) && @$by_lid[$account]) - { - return $by_lid[$account]; - } - } - - $acct_type = $this->get_type($account) ? $this->get_type($account) : $this->account_type; - - if ($acct_type == 'g' && $this->group_context) - { - $sri = ldap_search($this->ds, $this->group_context, $ldapgroup . '=' . abs($account)); - $groups = ldap_get_entries($this->ds, $sri); - } - $sri = ldap_search($this->ds, $this->user_context, $ldapacct . '=' . $account); - $users = ldap_get_entries($this->ds, $sri); - - if ($users[0]['dn']) - { - $in += 1; - } - if ($groups[0]['dn']) - { - $in += 2; - } - /* This sets up internal caching for this function */ - if($ldapgroup == 'gidnumber') - { - $by_id[$account] = $in; - $by_lid[$this->id2name($account)] = $in; - } - else - { - $by_lid[$account] = $in; - $by_id[$this->name2id($account)] = $in; - } - return $in; - } - - function create($account_info,$default_prefs=True) - { - /* echo '
in create for account_lid: "'.$account_lid.'"'; */ - if (empty($account_info['account_id']) || !$account_info['account_id']) - { - $account_id = $this->get_nextid($account_info['account_type']); - /* echo '
using'.$account_id;exit; */ - } - else - { - $account_id = abs($account_info['account_id']); - } - $entry['userpassword'] = $account_info['account_passwd']; - $entry['phpgwaccounttype'] = $account_info['account_type']; - $entry['phpgwaccountexpires'] = $account_info['account_expires']; - - if($account_info['account_type'] == 'g') - { - $sri = ldap_search($this->ds, $this->group_context, 'cn=' . (string)$account_info['account_lid']); - } - else - { - $sri = ldap_search($this->ds, $this->user_context, 'uid=' . (string)$account_info['account_lid']); - } - $allValues = ldap_get_entries($this->ds, $sri); - - if ($GLOBALS['egw_info']['server']['ldap_extra_attributes'] && $account_info['account_type'] != 'g') - { - $entry['homedirectory'] = $account_info['homedirectory'] && $account_info['homedirectory'] != $GLOBALS['egw_info']['server']['ldap_account_home'] ? $account_info['homedirectory'] : $GLOBALS['egw_info']['server']['ldap_account_home'].SEP.$account_info['account_lid']; - $entry['loginshell'] = $account_info['loginshell'] ? $account_info['loginshell'] : $GLOBALS['egw_info']['server']['ldap_account_shell']; - } - elseif($account_info['account_type'] != 'g') - { - $entry['homedirectory'] = '/home/'.$account_info['account_lid']; - $entry['loginshell'] = '/bin/false'; - } - - if ($allValues[0]['dn']) - { - /* This should keep the password from being overwritten here on ldap import */ - unset($entry['userpassword']); - $entry['gidnumber'] = $account_id; - - while (list($key,$val) = each($entry)) - { - $tmpentry = ''; - $tmpentry[$key] = trim($val); /* must trim! */ - /* echo '
'.$key.' '.$val; */ - if ($tmpentry[$key]) - { - if (!$allValues[0][$key][0]) - { - /* attribute was not in LDAP, add it */ - ldap_mod_add($this->ds, $allValues[0]['dn'], $tmpentry); - } - else - { - /* attribute was in LDAP, modify it */ - ldap_modify($this->ds, $allValues[0]['dn'], $tmpentry); - } - } - } - - if ($account_info['account_type'] == 'g') - { - $tmpentry['objectclass'] = $this->requiredObjectClasses['group']; - } - else - { - $tmpentry['objectclass'] = $this->requiredObjectClasses['user']; - $tmpentry['uidnumber'] = $account_id; - $tmpentry['userpassword'] = $GLOBALS['egw']->common->encrypt_password($account_info['account_passwd'],False); - $tmpentry['phpgwaccountstatus'] = $account_info['account_status']; - $tmpentry['phpgwaccounttype'] = $account_info['account_type']; - $tmpentry['phpgwaccountexpires'] = $account_info['account_expires']; - } - ldap_modify($this->ds, $allValues[0]['dn'], $tmpentry); - } - else - { - /* Not already there, we will add it */ - if ($account_info['account_type'] == 'g') - { - $dn = 'cn='.$account_info['account_lid'] . ',' . $this->group_context; - unset($entry['homedirectory']); - unset($entry['loginshell']); - unset($entry['userpassword']); - $entry['objectclass'] = $this->requiredObjectClasses['group']; - $entry['cn'] = $GLOBALS['egw']->translation->convert($account_info['account_lid'],$GLOBALS['egw']->translation->charset(),'utf-8'); - $entry['gidnumber'] = $account_id; - $entry['description'] = 'eGW-created group'; - } - else - { - $dn = 'uid=' . $account_info['account_lid'] . ',' . $this->user_context; - - $entry['cn'] = $GLOBALS['egw']->translation->convert( - sprintf( - "%s %s", - $account_info['account_firstname'], - $account_info['account_lastname'] - ), - $GLOBALS['egw']->translation->charset(), - 'utf-8' - ); - - $entry['sn'] = $GLOBALS['egw']->translation->convert( - $account_info['account_lastname'] ? $account_info['account_lastname'] : 'not set', - $GLOBALS['egw']->translation->charset(), - 'utf-8' - ); - - if($account_info['account_firstname']) - { - $entry['givenname'] = $GLOBALS['egw']->translation->convert( - $account_info['account_firstname'], - $GLOBALS['egw']->translation->charset(), - 'utf-8' - ); - } - if($account_info['account_email']) - { - $entry['mail'] = $GLOBALS['egw']->translation->convert( - $account_info['account_email'], - $GLOBALS['egw']->translation->charset(), - 'utf-8' - ); - } - $entry['uid'] = $account_info['account_lid']; - $entry['uidnumber'] = $account_id; - $entry['gidnumber'] = abs($account_info['account_primary_group']); - $entry['userpassword'] = $GLOBALS['egw']->common->encrypt_password($account_info['account_passwd']); - $entry['objectclass'] = $this->requiredObjectClasses['user']; - if($account_info['account_status']) - { - $entry['phpgwaccountstatus'] = $account_info['account_status']; - } - $entry['phpgwaccounttype'] = $account_info['account_type']; - $entry['phpgwaccountexpires'] = $account_info['account_expires']; - } - - #_debug_array($entry); - - // stop processing if ldap_add fails - if(!@ldap_add($this->ds, $dn, $entry)) - { - if ($account_info['account_type'] != 'g') - { - return false; - } - // try again with namedObject added, in case we have the newer schema which eg. SuSE uses - // in which our required objectclasses for a group, have no structural object - if ($account_info['account_type'] == 'g') - { - $entry['objectclass'][] = 'namedObject'; - - if (!@ldap_add($this->ds, $dn, $entry)) - { - return false; - } - } - } - } - // print ldap_error($this->ds); - - // lets check group the user needs to be added - if($account_info['account_type'] == 'u') - { - @settype($account_info['account_groups'],'array'); - foreach($account_info['account_groups'] as $key => $value) - { - // search for the group - $filter = 'gidnumber=' . abs($value); - $justThese = array('memberuid'); - $sri = ldap_search($this->ds, $this->group_context, $filter, $justThese); - if($sri) - { - $allValues = ldap_get_entries($this->ds, $sri); - // if the user is not member of this group, add him - if(is_array($allValues[0]['memberuid'])) - { - // this group has already some members - if(!in_array($account_info['account_lid'],$allValues[0]['memberuid'])) - { - $dn = $allValues[0]['dn']; - $newData = array(); - $newData['memberuid'] = $allValues[0]['memberuid']; - unset($newData['memberuid']['count']); - $newData['memberuid'][] = $account_info['account_lid']; - $newData['memberuid'] = array_unique($newData['memberuid']); - ldap_mod_replace($this->ds, $dn, $newData); - #print ldap_error($this->ds)."
"; - } - } - else - { - // this group has no members - $dn = $allValues[0]['dn']; - $newData = array(); - $newData['memberuid'][] = $account_info['account_lid']; - ldap_mod_replace($this->ds, $dn, $newData); - } - } - } - } - - if($account_id && is_object($GLOBALS['egw']->preferences) && $default_prefs) - { - $GLOBALS['egw']->preferences->create_defaults($account_id); - } - - if($account_info['account_type'] == 'g') - { - return -$account_id; - } - else - { - return $account_id; - } - } - - function auto_add($accountname, $passwd, $default_prefs = False, $default_acls = False, $expiredate = 0, $account_status = 'A') - { - if ($expiredate == 0) - { - if(isset($GLOBALS['egw_info']['server']['auto_create_expire']) == True) - { - if($GLOBALS['egw_info']['server']['auto_create_expire'] == 'never') - { - $expires = -1; - } - else - { - $expiredate = time() + $GLOBALS['egw_info']['server']['auto_create_expire']; - } - } - } - else - { - /* expire in 30 days by default */ - $expiredate = time() + ((60 * 60) * (30 * 24)); - } - - if ($expires != -1) - { - $expires = mktime(2,0,0,date('n',$expiredate), (int)date('d',$expiredate), date('Y',$expiredate)); - } - - $default_group_id = $this->name2id($GLOBALS['egw_info']['server']['default_group_lid']); - if (!$default_group_id) - { - $default_group_id = abs((int)$this->name2id('Default')); - } - $primary_group = $GLOBALS['auto_create_acct']['primary_group'] && - $this->get_type((int)$GLOBALS['auto_create_acct']['primary_group']) == 'g' ? - (int)$GLOBALS['auto_create_acct']['primary_group'] : $default_group_id; - - $acct_info = array( - 'account_lid' => $accountname, - 'account_type' => 'u', - 'account_passwd' => $passwd, - 'account_firstname' => $GLOBALS['auto_create_acct']['firstname'] ? $GLOBALS['auto_create_acct']['firstname'] : 'New', - 'account_lastname' => $GLOBALS['auto_create_acct']['lastname'] ? $GLOBALS['auto_create_acct']['lastname'] : 'User', - 'account_status' => $account_status, - 'account_expires' => $expires, - 'account_primary_group' => $primary_group, - ); - - /* attempt to set an email address */ - if (isset($GLOBALS['auto_create_acct']['email']) == True && $GLOBALS['auto_create_acct']['email'] != '') - { - $acct_info['account_email'] = $GLOBALS['auto_create_acct']['email']; - } - elseif(isset($GLOBALS['egw_info']['server']['mail_suffix']) == True && $GLOBALS['egw_info']['server']['mail_suffix'] != '') - { - $acct_info['account_email'] = $accountname . '@' . $GLOBALS['egw_info']['server']['mail_suffix']; - } - - $this->db->transaction_begin(); - - $this->create($acct_info,$default_prefs); /* create the account */ - - $accountid = $this->name2id($accountname); /* grab the account id or an error code */ - - if ($accountid) /* begin account setup */ - { - if($primary_group) - { - $GLOBALS['egw']->acl->add_repository('phpgw_group', $primary_group,$accountid,1); - } - - /* if we have an mail address set it in the users' email preference */ - if (isset($GLOBALS['auto_create_acct']['email']) && $GLOBALS['auto_create_acct']['email'] != '') - { - $GLOBALS['egw']->acl->acl($accountid); /* needed als preferences::save_repository calls acl */ - $GLOBALS['egw']->preferences->preferences($accountid); - $GLOBALS['egw']->preferences->read_repository(); - $GLOBALS['egw']->preferences->add('email','address',$GLOBALS['auto_create_acct']['email']); - $GLOBALS['egw']->preferences->save_repository(); - } - /* use the default mail domain to set the uesrs' email preference */ - elseif(isset($GLOBALS['egw_info']['server']['mail_suffix']) && $GLOBALS['egw_info']['server']['mail_suffix'] != '') - { - $GLOBALS['egw']->acl->acl($accountid); /* needed als preferences::save_repository calls acl */ - $GLOBALS['egw']->preferences->preferences($accountid); - $GLOBALS['egw']->preferences->read_repository(); - $GLOBALS['egw']->preferences->add('email','address', $accountname . '@' . $GLOBALS['egw_info']['server']['mail_suffix']); - $GLOBALS['egw']->preferences->save_repository(); - } - - /* commit the new account transaction */ - $this->db->transaction_commit(); - - // call hook to notify interested apps about the new account - $GLOBALS['hook_values']['account_lid'] = $acct_info['account_lid']; - $GLOBALS['hook_values']['account_id'] = $accountid; - $GLOBALS['hook_values']['new_passwd'] = $acct_info['account_passwd']; - $GLOBALS['hook_values']['account_status'] = $acct_info['account_status']; - $GLOBALS['hook_values']['account_firstname'] = $acct_info['account_firstname']; - $GLOBALS['hook_values']['account_lastname'] = $acct_info['account_lastname']; - $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( - 'location' => 'addaccount', - // at login-time only the hooks from the following apps will be called - 'order' => array('felamimail','fudforum'), - ),False,True); // called for every app now, not only enabled ones - } /* end account setup */ - else /* if no account id abort the account creation */ - { - $this->db->transaction_abort(); - } - - /* - * If we succeeded in creating the account (above), return the accountid, else, - * return the error value from $this->name2id($accountname) - */ - return $accountid; - } /* end auto_add() */ - - function get_account_name($account_id,&$lid,&$fname,&$lname) - { - $acct_type = $this->get_type($account_id); - - /* search the dn for the given uid */ - if(($acct_type == 'g') && $this->group_context) - { - $sri = ldap_search($this->ds, $this->group_context, 'gidnumber=' . abs((int)$account_id)); - } - else - { - $sri = ldap_search($this->ds, $this->user_context, 'uidnumber=' . (int)$account_id); - } - $allValues = ldap_get_entries($this->ds, $sri); - - if($acct_type =='g') - { - $lid = $GLOBALS['egw']->translation->convert($allValues[0]['cn'][0],'utf-8'); - $fname = $GLOBALS['egw']->translation->convert($allValues[0]['cn'][0],'utf-8'); - $lname = lang('Group'); - } - else - { - $lid = $GLOBALS['egw']->translation->convert($allValues[0]['uid'][0],'utf-8'); - $fname = $GLOBALS['egw']->translation->convert($allValues[0]['givenname'][0],'utf-8'); - $lname = $GLOBALS['egw']->translation->convert($allValues[0]['sn'][0],'utf-8'); - } - return !empty($lid); - } - - function getDNforID($_accountid = '') - { - $_account_id = get_account_id($_accountid); - - $sri = ldap_search($this->ds, $this->user_context, 'uidnumber=' . (int)$_account_id); - $allValues = ldap_get_entries($this->ds, $sri); - - return $allValues[0]['dn']; - } - - /** - * Update the last login timestamps and the IP - * - * @param int $account_id - * @param string $ip - * @return int lastlogin time - */ - function update_lastlogin($_account_id, $ip) - { - $entry['phpgwaccountlastlogin'] = time(); - $entry['phpgwaccountlastloginfrom'] = $ip; - - $sri = ldap_search($this->ds, $GLOBALS['egw_info']['server']['ldap_context'], 'uidnumber=' . (int)$_account_id); - $allValues = ldap_get_entries($this->ds, $sri); - - $dn = $allValues[0]['dn']; - @ldap_modify($this->ds, $dn, $entry); + return $this->_read_user($account_id); + } + + /** + * Saves / adds the data of one account + * + * If no account_id is set in data the account is added and the new id is set in $data. + * + * @param array $data array with account-data + * @return int/boolean the account_id or false on error + */ + function save(&$data) + { + $is_group = $data['account_id'] < 0 || $data['type'] == 'g'; + + $data_utf8 = $this->translation->convert($data,$this->translation->charset(),'utf-8'); - return $allValues[0]['phpgwaccountlastlogin'][0]; + // common code for users and groups + // checks if accout_lid (dn) has been changed or required objectclass'es are missing + if ($data_utf8['account_id'] && $data_utf8['account_lid']) + { + // read the entry first, to check if the dn (account_lid) has changed + $sri = $is_group ? ldap_search($this->ds,$this->group_context,'gidnumber='.abs($data['account_id'])) : + ldap_search($this->ds,$this->user_context,'uidnumber='.$data['account_id']); + $old = ldap_get_entries($this->ds, $sri); + + if (!$old['count']) + { + unset($old); + } + else + { + $old = $this->_ldap2array($old[0]); + foreach($old['objectclass'] as $n => $class) + { + $old['objectclass'][$n] = strtolower($class); + } + if ($is_group && ($old['cn'] != $data_utf8['account_lid'] || substr($old['dn'],0,3) != 'cn=') || + !$is_group && ($old['uid'] != $data_utf8['account_lid'] || substr($old['dn'],0,4) != 'uid=')) + { + // query the memberships to set them again later + if (!$is_group) $memberships = $this->memberships($data['account_id']); + + // if dn has changed --> delete the old entry, as we cant rename the dn + // $this->delete would call accounts::delete, which will delete als ACL of the user too! + accounts_backend::delete($data['account_id']); + unset($old['dn']); + $to_write = $old; + unset($old); + } + } + } + if (!$data['account_id']) // new group + { + if (!($data['account_id'] = $data_utf8['account_id'] = $this->_get_nextid($is_group ? 'g' : 'u'))) + { + return false; + } + } + // check if we need to write the objectclass: new entry or required object classes are missing + if (!$old || array_diff($this->requiredObjectClasses[$is_group ? 'group' : 'user'],$old['objectclass'])) + { + // additional objectclasse might be already set in $to_write or $old + if (!is_array($to_write['objectclass'])) + { + $to_write['objectclass'] = $old ? $old['objectclass'] : array(); + } + $to_write['objectclass'] = array_values(array_unique(array_merge($to_write['objectclass'], + $this->requiredObjectClasses[$is_group ? 'group' : 'user']))); + } + if (!($dn = $old['dn'])) + { + if (!$data['account_lid']) return false; + + $dn = $is_group ? 'cn='.$data_utf8['account_lid'].','.$this->group_context : + 'uid='.$data_utf8['account_lid'].','.$this->user_context; + } + // now we merge the user or group data + if ($is_group) + { + $to_write = $this->_merge_group($to_write,$data_utf8); + $data['account_type'] = 'g'; + } + else + { + $to_write = $this->_merge_user($to_write,$data_utf8,!$old); + $data['account_type'] = 'u'; + } + echo "

ldap_".($old ? 'modify' : 'add')."(,$dn,".print_r($to_write,true).")

\n"; + // modifying or adding the entry + if ($old && !ldap_modify($this->ds,$dn,$to_write) || + !$old && !ldap_add($this->ds,$dn,$to_write)) + { + echo ldap_error($this->ds); exit; + return false; + } + if ($memberships) // setting the previous memberships of the renamed account + { + $this->set_memberships($memberships,$data['account_id']); + } + return $data['account_id']; + } + + /** + * Convert a single ldap value into a associative array + * + * @param array $ldap array with numerical and associative indexes and count's + * @return array with only associative index and no count's + */ + function _ldap2array($ldap) + { + if (!is_array($ldap)) return false; + + $arr = array(); + foreach($ldap as $var => $val) + { + if (is_int($var) || $var == 'count') continue; + + if (is_array($val) && $val['count'] == 1) + { + $arr[$var] = $val[0]; + } + else + { + if (is_array($val)) unset($val['count']); + + $arr[$var] = $val; + } + } + return $arr; + } + + + /** + * Delete one account, deletes also all acl-entries for that account + * + * @param int $id numeric account_id + * @return boolean true on success, false otherwise + */ + function delete($account_id) + { + if (!(int)$account_id) return false; + + if ($account_id < 0) + { + $sri = ldap_search($this->ds, $this->group_context, 'gidnumber=' . abs($account_id)); + } + else + { + // remove the user's memberships + $this->set_memberships(array(),$account_id); + + $sri = ldap_search($this->ds, $this->user_context, 'uidnumber=' . $account_id); + } + if (!$sri) return false; + + $allValues = ldap_get_entries($this->ds, $sri); + if (!$allValues['count']) return false; + + return ldap_delete($this->ds, $allValues[0]['dn']); + } + + /** + * Reads the data of one group + * + * @internal + * @param int $account_id numeric account-id (< 0 as it's for a group) + * @return array/boolean array with account data (keys: account_id, account_lid, ...) or false if account not found + */ + function _read_group($account_id) + { + $sri = ldap_search($this->ds, $this->group_context, 'gidnumber=' . abs($account_id), + array('dn','gidnumber','cn')); + + $data = ldap_get_entries($this->ds, $sri); + if (!$data['count']) + { + return false; // group not found + } + $data = $this->translation->convert($data[0],'utf-8'); + + return array( + 'account_dn' => $data['dn'], + 'account_id' => -$data['gidnumber'][0], + 'account_lid' => $data['cn'][0], + 'account_type' => 'g', + 'account_firstname' => $data['cn'][0], + 'account_lastname' => lang('Group'), + ); + } + + /** + * Reads the data of one user + * + * @internal + * @param int $account_id numeric account-id + * @return array/boolean array with account data (keys: account_id, account_lid, ...) or false if account not found + */ + function _read_user($account_id) + { + $sri = ldap_search($this->ds, $this->user_context, 'uidnumber=' . $account_id, + array('dn','uidnumber','uid','gidnumber','givenname','sn','cn','mail', + 'shadowexpire','shadowlastchange','homedirectory','loginshell')); + + $data = ldap_get_entries($this->ds, $sri); + if (!$data['count']) + { + return false; // user not found + } + $data = $this->translation->convert($data[0],'utf-8'); + + $user = array( + 'account_dn' => $data['dn'], + 'account_id' => (int)$data['uidnumber'][0], + 'account_lid' => $data['uid'][0], + 'account_type' => 'u', + 'account_primary_group' => -$data['gidnumber'][0], + 'account_firstname' => $data['givenname'][0], + 'account_lastname' => $data['sn'][0], + 'account_email' => $data['mail'][0], + 'account_fullname' => $data['cn'][0], + // both status and expires are encoded in the single shadowexpire value in LDAP + // - if it's unset an account is enabled AND does never expire + // - if it's set to 0, the account is disabled + // - if it's set to > 0, it will or already has expired --> acount is active if it not yet expired + // shadowexpire is in days since 1970/01/01 (equivalent to a timestamp / (24*60*60) + 'account_status' => isset($data['shadowexpire']) && $data['shadowexpire'][0]*24*3600 < time() ? false : 'A', + 'account_expires' => isset($data['shadowexpire']) && $data['shadowexpire'][0] ? $data['shadowexpire'][0]*24*3600 : -1, + 'account_lastpasswd_change' => isset($data['shadowlastchange']) ? $data['shadowlastchange'][0]*24*3600 : null, + // lastlogin and lastlogin from are not availible via the shadowAccount object class + // 'account_lastlogin' => $data['phpgwaccountlastlogin'][0], + // 'account_lastloginfrom' => $data['phpgwaccountlastloginfrom'][0], + ); + if ($GLOBALS['egw_info']['server']['ldap_extra_attributes']) + { + $user['homedirectory'] = $data['homedirectory'][0]; + $user['loginshell'] = $data['loginshell'][0]; + } + return $user; + } + + /** + * Merges the group releavant account data from $data into $to_write + * + * @internal + * @param array $to_write data to write to ldap incl. objectclass ($data is NOT yet merged) + * @param array $data array with account-data in utf-8 + * @return array merged data + */ + function _merge_group($to_write,$data) + { + $to_write['gidnumber'] = abs($data['account_id']); + $to_write['cn'] = $data['account_lid']; + + return $to_write; + } + + /** + * Merges the user releavant account data from $data into $to_write + * + * @internal + * @param array $to_write data to write to ldap incl. objectclass ($data is NOT yet merged) + * @param array $data array with account-data in utf-8 + * @param boolean $new_entry + * @return array merged data + */ + function _merge_user($to_write,$data,$new_entry) + { + //echo "

accounts_ldap::_merge_user(".print_r($to_write,true).','.print_r($data,true).",$new_entry)

\n"; + + $to_write['uidnumber'] = $data['account_id']; + $to_write['uid'] = $data['account_lid']; + $to_write['gidnumber'] = abs($data['account_primary_group']); + $to_write['givenname'] = $data['account_firstname']; + $to_write['sn'] = $data['account_lastname']; + $to_write['mail'] = (string) $data['account_email']; + $to_write['cn'] = $data['account_fullname'] ? $data['account_fullname'] : $data['account_firstname'].' '.$data['account_lastname']; + + if (isset($data['account_passwd']) && $data['account_passwd']) + { + if(!@is_object($GLOBALS['egw']->auth)) + { + $GLOBALS['egw']->auth =& CreateObject('phpgwapi.auth'); + } + if (!preg_match('/^\\{[a-z5]{3,5}\\}.+/i',$data['account_passwd'])) // if it's not already entcrypted, do so now + { + $data['account_passwd'] = $GLOBALS['egw']->auth->encrypt_ldap($data['account_passwd']); + } + $to_write['userpassword'] = $data['account_passwd']; + } + // both status and expires are encoded in the single shadowexpire value in LDAP + // - if it's unset an account is enabled AND does never expire + // - if it's set to 0, the account is disabled + // - if it's set to > 0, it will or already has expired --> acount is active if it not yet expired + // shadowexpire is in days since 1970/01/01 (equivalent to a timestamp / (24*60*60) + $to_write['shadowexpire'] = !$data['account_status'] ? + ($data['account_expires'] != -1 && $data['account_expires'] < time() ? $data['account_expires']/(24*3600) : 0) : + ($data['account_expires'] != -1 ? $data['account_expires']/(24*3600) : array()); // array() = unset value + + if ($new_entry && is_array($to_write['shadowexpire']) && !count($to_write['shadowexpire'])) + { + unset($to_write['shadowexpire']); // gives protocoll error otherwise + } + + if ($data['account_lastpasswd_change']) $to_write['shadowlastchange'] = $data['lastpasswd_change']/(24*3600); + + // lastlogin and lastlogin from are not availible via the shadowAccount object class + // $to_write['phpgwaccountlastlogin'] = $data['lastlogin']; + // $to_write['phpgwaccountlastloginfrom'] = $data['lastloginfrom']; + + if ($GLOBALS['egw_info']['server']['ldap_extra_attributes']) + { + if (isset($data['homedirectory'])) $to_write['homedirectory'] = $data['homedirectory']; + if (isset($data['loginshell'])) $to_write['loginshell'] = $data['loginshell'] ? $data['loginshell'] : array(); + } + if ($new_entry && !isset($to_write['homedirectory'])) + { + $to_write['homedirectory'] = '/dev/null'; // is a required attribute of posixAccount + } + return $to_write; + } + + /** + * Searches users and/or groups + * + * ToDo: implement a search like accounts::search + * + * @param string $_type + * @param int $start=null + * @param string $sort='' + * @param string $order='' + * @param string $query + * @param int $offset=null + * @param string $query_type + * @return array + */ + function get_list($_type='both', $start = '',$sort = '', $order = '', $query = '', $offset = null, $query_type='') + { + //print "\$_type=$_type, \$start=$start , \$sort=$sort, \$order=$order, \$query=$query, \$offset=$offset, \$query_type=$query_type
"; + $query = strtolower($query); + + if($_type != 'groups') + { + $filter = "(&(uidnumber=*)(objectclass=posixaccount)"; + if (!empty($query) && $query != '*') + { + switch($query_type) + { + case 'all': + default: + $query = '*'.$query; + // fall-through + case 'start': + $query .= '*'; + // fall-through + case 'exact': + $filter .= "(|(uid=$query)(sn=$query)(cn=$query)(givenname=$query)(mail=$query))"; + break; + case 'firstname': + case 'lastname': + case 'lid': + case 'email': + $to_ldap = array( + 'firstname' => 'givenname', + 'lastname' => 'sn', + 'lid' => 'uid', + 'email' => 'mail', + ); + $filter .= '('.$to_ldap[$query_type].'=*'.$query.'*)'; + break; + } + } + $filter .= ')'; + + $sri = ldap_search($this->ds, $this->user_context, $filter); + $allValues = ldap_get_entries($this->ds, $sri); + while (list($null,$allVals) = @each($allValues)) + { + settype($allVals,'array'); + $test = @$allVals['uid'][0]; + if (!$GLOBALS['egw_info']['server']['global_denied_users'][$test] && $allVals['uid'][0]) + { + $accounts[] = Array( + 'account_id' => $allVals['uidnumber'][0], + 'account_lid' => $GLOBALS['egw']->translation->convert($allVals['uid'][0],'utf-8'), + 'account_type' => 'u', + 'account_firstname' => $GLOBALS['egw']->translation->convert($allVals['givenname'][0],'utf-8'), + 'account_lastname' => $GLOBALS['egw']->translation->convert($allVals['sn'][0],'utf-8'), + 'account_status' => isset($allVals['shadowexpire'][0]) && $allVals['shadowexpire'][0]/(24*3600) < time() ? false : 'A', + 'account_email' => $allVals['mail'][0], + ); + } + } + } + if ($_type != 'accounts') + { + if(empty($query) || $query == '*') + { + $filter = '(&(gidnumber=*)(objectclass=posixgroup))'; + } + else + { + $filter = "(&(gidnumber=*)(objectclass=posixgroup)(|(cn=*$query*)))"; + } + $sri = ldap_search($this->ds, $this->group_context, $filter); + $allValues = ldap_get_entries($this->ds, $sri); + while (list($null,$allVals) = @each($allValues)) + { + settype($allVals,'array'); + $test = $allVals['cn'][0]; + if (!$GLOBALS['egw_info']['server']['global_denied_groups'][$test] && $allVals['cn'][0]) + { + $accounts[] = Array( + 'account_id' => -$allVals['gidnumber'][0], + 'account_lid' => $GLOBALS['egw']->translation->convert($allVals['cn'][0],'utf-8'), + 'account_type' => 'g', + 'account_firstname' => $GLOBALS['egw']->translation->convert($allVals['cn'][0],'utf-8'), + 'account_lastname' => lang('Group'), + 'account_status' => 'A', + ); + } + } + } + // sort the array + $arrayFunctions =& CreateObject('phpgwapi.arrayfunctions'); + if(empty($order)) + { + $order = 'account_lid'; + } + $sortedAccounts = $arrayFunctions->arfsort($accounts,explode(',',$order),$sort); + $this->total = count($accounts); + // return only the wanted accounts + if (is_array($sortedAccounts)) + { + reset($sortedAccounts); + if(is_numeric($start) && is_numeric($offset)) + { + return array_slice($sortedAccounts, $start, $offset); + } + elseif(is_numeric($start)) + { + return array_slice($sortedAccounts, $start, $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']); + } + else + { + return $sortedAccounts; + } + } + return False; + } + + /** + * convert an alphanumeric account-value (account_lid, account_email) to the account_id + * + * Please note: + * - if a group and an user have the same account_lid the group will be returned (LDAP only) + * - if multiple user have the same email address, the returned user is undefined + * + * @param string $name value to convert + * @param string $which='account_lid' type of $name: account_lid (default), account_email, person_id, account_fullname + * @param string $account_type u = user, g = group, default null = try both + * @return int/false numeric account_id or false on error ($name not found) + */ + function name2id($name,$which='account_lid',$account_type=null) + { + $name = $this->translation->convert($name,$this->translation->charset(),'utf-8'); + + if ($which == 'account_lid' && $account_type !== 'u') // groups only support account_lid + { + + $sri = ldap_search($this->ds, $this->group_context, '(&(cn=' . $name . ')(objectclass=posixgroup))'); + $allValues = ldap_get_entries($this->ds, $sri); + + if (@$allValues[0]['gidnumber'][0]) + { + return -$allValues[0]['gidnumber'][0]; + } + } + $to_ldap = array( + 'account_lid' => 'uid', + 'account_email' => 'mail', + 'account_fullname' => 'cn', + ); + if (!isset($to_ldap[$which]) || $account_type === 'g') return False; + + $sri = ldap_search($this->ds, $this->user_context, '(&('.$to_ldap[$which].'=' . $name . ')(objectclass=posixaccount))'); + + $allValues = ldap_get_entries($this->ds, $sri); + + if (@$allValues[0]['uidnumber'][0]) + { + return (int)$allValues[0]['uidnumber'][0]; + } + return False; + } + + /** + * Update the last login timestamps and the IP + * + * @param int $account_id + * @param string $ip + * @return int lastlogin time + */ + function update_lastlogin($_account_id, $ip) + { + return false; // not longer supported + + $entry['phpgwaccountlastlogin'] = time(); + $entry['phpgwaccountlastloginfrom'] = $ip; + + $sri = ldap_search($this->ds, $GLOBALS['egw_info']['server']['ldap_context'], 'uidnumber=' . (int)$_account_id); + $allValues = ldap_get_entries($this->ds, $sri); + + $dn = $allValues[0]['dn']; + @ldap_modify($this->ds, $dn, $entry); + + return $allValues[0]['phpgwaccountlastlogin'][0]; + } + + /** + * Query memberships of a given account + * + * @param int $account_id + * @return array/boolean array with account_id => account_lid pairs or false if account not found + */ + function memberships($account_id) + { + if (!(int) $account_id || !($account_lid = $this->id2name($account_id))) return false; + + $sri = ldap_search($this->ds,$this->group_context,"(&(objectClass=posixGroup)(memberuid=$account_lid))",array('cn','gidnumber')); + $memberships = array(); + foreach(ldap_get_entries($this->ds, $sri) as $key => $data) + { + if ($key === 'count') continue; + + $memberships[(string) -$data['gidnumber'][0]] = $data['cn'][0]; + } + //echo "accounts::memberships($account_id)"; _debug_array($memberships); + return $memberships; + } + + /** + * Query the members of a group + * + * @param int $gid + * @return array with uidnumber => uid pairs + */ + function members($gid) + { + if (!is_numeric($gid)) return false; + + $gid = abs($gid); // our gid is negative! + + $sri = ldap_search($this->ds,$this->group_context,"(&(objectClass=posixGroup)(gidnumber=$gid))",array('memberuid')); + $group = ldap_get_entries($this->ds, $sri); + + $members = array(); + if (isset($group[0]['memberuid'])) + { + foreach($group[0]['memberuid'] as $lid) + { + if (($id = $this->name2id($lid))) + { + $members[$id] = $lid; + } + } + } + //echo "accounts_ldap::members($gid)"; _debug_array($members); + return $members; + } + + /** + * Sets the memberships of the given account + * + * @param array $groups array with gidnumbers + * @param int $accountid uidnumber + */ + function set_memberships($groups,$accountid) + { + //echo "

accounts_ldap::set_memberships(".print_r($groups,true).",$accountid)

\n"; + + // remove not longer existing memberships + if (($old_memberships = $this->memberships($accountid))) + { + $old_memberships = array_keys($old_memberships); + foreach(array_diff($old_memberships,$groups) as $gid) + { + if (($members = $this->members($gid))) + { + unset($members[$this->account_id]); + $this->set_members($members,$gid); + } + } + } + // adding new memberships + foreach($old_memberships ? array_diff($groups,$old_memberships) : $groups as $gid) + { + $members = $this->members($gid); + $members[$accountid] = $this->id2name($accountid); + $this->set_members($members,$gid); } } + + /** + * Set the members of a group + * + * @param array $members array with uidnumber or uid's + * @param int $gid gidnumber of group to set + */ + function set_members($members,$gid) + { + //echo "

accounts_ldap::set_members(".print_r($members,true).",$gid)

\n"; + if (!($cn = $this->id2name($gid))) return false; + + foreach($members as $key => $member) + { + if (is_numeric($member)) + { + $members[$key] = $this->id2name($member); + } + } + ldap_modify($this->ds,'cn='.$cn.','.$this->group_context,array('memberUid' => array_values($members))); + } + + /** + * Using the common functions next_id and last_id, find the next available account_id + * + * @internal + * @param $string $account_type='u' (optional, default to 'u') + * @return int/boolean interger account_id or false if none is free anymore + */ + function _get_nextid($account_type='u') + { + $min = $GLOBALS['egw_info']['server']['account_min_id'] ? $GLOBALS['egw_info']['server']['account_min_id'] : 0; + $max = $GLOBALS['egw_info']['server']['account_max_id'] ? $GLOBALS['egw_info']['server']['account_max_id'] : 0; + + if ($account_type == 'g') + { + $type = 'groups'; + $sign = -1; + } + else + { + $type = 'accounts'; + $sign = 1; + } + /* Loop until we find a free id */ + do + { + $account_id = (int) $GLOBALS['egw']->common->next_id($type,$min,$max); + } + while ($account_id && $this->exists($sign * $account_id)); // check need to include the sign! + + if (!$account_id || $GLOBALS['egw_info']['server']['account_max_id'] && + $account_id > $GLOBALS['egw_info']['server']['account_max_id']) + { + return False; + } + return $account_id; + } +} diff --git a/phpgwapi/inc/class.accounts_sql.inc.php b/phpgwapi/inc/class.accounts_sql.inc.php index 1ec98805ae..fb40981c62 100644 --- a/phpgwapi/inc/class.accounts_sql.inc.php +++ b/phpgwapi/inc/class.accounts_sql.inc.php @@ -1,483 +1,385 @@ * - * and Dan Kuykendall * - * and Bettina Gille [ceb@phpgroupware.org] * - * View and manipulate account records using SQL * - * Copyright (C) 2000 - 2002 Joseph Engo * - * Copyright (C) 2003 Joseph Engo, Bettina Gille * - * ------------------------------------------------------------------------ * - * This library is part of the eGroupWare API * - * http://www.egroupware.org * - * ------------------------------------------------------------------------ * - * This library is free software; you can redistribute it and/or modify it * - * under the terms of the GNU Lesser General Public License as published by * - * the Free Software Foundation; either version 2.1 of the License, * - * or any later version. * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * - * See the GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License * - * along with this library; if not, write to the Free Software Foundation, * - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - \**************************************************************************/ - /* $Id$ */ +/** + * API - accounts SQL backend + * + * The SQL backend stores the group memberships via the ACL class (location 'phpgw_group') + * + * The (positive) account_id's of groups are mapped in this class to negative numeric + * account_id's, to conform wit the way we handle groups in LDAP! + * + * @link http://www.egroupware.org + * @author Ralf Becker complete rewrite in 6/2006 and + * earlier to use the new DB functions + * + * This class replaces the former accounts_sql class written by + * Joseph Engo , Dan Kuykendall + * and Bettina Gille . + * Copyright (C) 2000 - 2002 Joseph Engo + * Copyright (C) 2003 Lars Kneschke, Bettina Gille + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @subpackage accounts + * @version $Id$ + */ + +/** + * SQL Backend for accounts + * + * @author Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @subpackage accounts + * @access internal only use the interface provided by the accounts class + */ +class accounts_backend +{ + /** + * instance of the db class + * + * @var object + */ + var $db; + /** + * table name for the accounts + * + * @var string + */ + var $table = 'egw_accounts'; + /** + * total number of found entries from get_list method + * + * @var int + */ + var $total; + + function accounts_backend() + { + if (is_object($GLOBALS['egw_setup']->db)) + { + $this->db = clone($GLOBALS['egw_setup']->db); + } + else + { + $this->db = clone($GLOBALS['egw']->db); + } + $this->db->set_app('phpgwapi'); // to load the right table-definitions for insert, select, update, ... + } /** - * Class for handling user and group accounts in SQL + * Reads the data of one account + * + * @param int $account_id numeric account-id + * @return array/boolean array with account data (keys: account_id, account_lid, ...) or false if account not found */ - class accounts_ + function read($account_id) { - var $db; - var $account_id; - var $data; - var $total; - var $table = 'egw_accounts'; - - function accounts_() - { - } - - function list_methods($_type='xmlrpc') - { - if (is_array($_type)) - { - $_type = $_type['type'] ? $_type['type'] : $_type[0]; - } - - switch($_type) - { - case 'xmlrpc': - $xml_functions = array( - 'get_list' => array( - 'function' => 'get_list', - 'signature' => array(array(xmlrpcStruct)), - 'docstring' => lang('Returns a full list of accounts on the system. Warning: This is return can be quite large') - ), - 'list_methods' => array( - 'function' => 'list_methods', - 'signature' => array(array(xmlrpcStruct,xmlrpcString)), - 'docstring' => lang('Read this list of methods.') - ) - ); - return $xml_functions; - break; - case 'soap': - return $this->soap_functions; - break; - default: - return array(); - break; - } - } - - /** - * grabs the records from the data store - * - */ - function read_repository() - { - $this->db->select($this->table,'*',array('account_id'=>abs($this->account_id)),__LINE__,__FILE__); - - $this->data['account_id'] = $this->db->next_record() ? $this->account_id : null; - $this->data['account_lid'] = $this->data['userid'] = $this->db->f('account_lid'); - $this->data['firstname'] = $this->db->f('account_firstname'); - $this->data['lastname'] = $this->db->f('account_lastname'); - $this->data['fullname'] = $this->db->f('account_firstname') . ' ' . $this->db->f('account_lastname'); - $this->data['lastlogin'] = $this->db->f('account_lastlogin'); - $this->data['lastloginfrom'] = $this->db->f('account_lastloginfrom'); - $this->data['lastpasswd_change'] = $this->db->f('account_lastpwd_change'); - $this->data['status'] = $this->db->f('account_status'); - $this->data['expires'] = $this->db->f('account_expires'); - $this->data['person_id'] = $this->db->f('person_id'); - $this->data['account_primary_group'] = $this->db->f('account_primary_group'); - $this->data['email'] = $this->db->f('account_email'); - - return $this->data; - } - - /** - * saves the records to the data store - * - */ - function save_repository() - { - $data = array( - 'account_firstname' => $this->data['firstname'], - 'account_lastname' => $this->data['lastname'], - 'account_status' => $this->data['status'], - 'account_expires' => $this->data['expires'], - 'account_lid' => $this->data['account_lid'], - 'account_primary_group' => $this->data['account_primary_group'], - 'account_email' => $this->data['email'], - ); - // overwrite person_id only if it's set in this->data! - if (isset($this->data['person_id'])) - { - $data['person_id'] = $this->data['person_id']; - } - $this->db->update($this->table,$data,array( - 'account_id' => abs($this->account_id) - ),__LINE__,__FILE__); - } - - function delete($accountid = '') - { - $account_id = get_account_id($accountid); - - /* Do this last since we are depending upon this record to get the account_lid above */ - $this->db->lock(Array($this->table)); - $this->db->delete($this->table,array('account_id'=>abs($account_id)),__LINE__,__FILE__); - $this->db->unlock(); - } - - function get_list($_type='both',$start = '',$sort = '', $order = '', $query = '', $offset = '',$query_type='') - { - if (! $sort) - { - $sort = "DESC"; - } - - if (!empty($order) && preg_match('/^[a-zA-Z_0-9, ]+$/',$order) && (empty($sort) || preg_match('/^(DESC|ASC|desc|asc)$/',$sort))) - { - $orderclause = "ORDER BY $order $sort"; - } - else - { - $orderclause = "ORDER BY account_lid ASC"; - } - - switch($_type) - { - case 'accounts': - $whereclause = "WHERE account_type = 'u'"; - break; - case 'groups': - $whereclause = "WHERE account_type = 'g'"; - break; - default: - $whereclause = ''; - } - - if ($query) - { - if ($whereclause) - { - $whereclause .= ' AND ( '; - } - else - { - $whereclause = ' WHERE ( '; - } - switch($query_type) - { - case 'all': - default: - $query = '%'.$query; - // fall-through - case 'start': - $query .= '%'; - // fall-through - case 'exact': - $query = $this->db->quote($query); - $whereclause .= " account_firstname LIKE $query OR account_lastname LIKE $query OR account_lid LIKE $query )"; - break; - case 'firstname': - case 'lastname': - case 'lid': - case 'email': - $query = $this->db->quote('%'.$query.'%'); - $whereclause .= " account_$query_type LIKE $query )"; - break; - } - } - - $sql = "SELECT * FROM $this->table $whereclause $orderclause"; - if ($offset) - { - $this->db->limit_query($sql,$start,__LINE__,__FILE__,$offset); - } - elseif (is_numeric($start)) - { - $this->db->limit_query($sql,$start,__LINE__,__FILE__); - } - else - { - $this->db->query($sql,__LINE__,__FILE__); - } - while ($this->db->next_record()) - { - $accounts[] = Array( - 'account_id' => ($this->db->f('account_type') == 'g' ? -1 : 1) * $this->db->f('account_id'), - 'account_lid' => $this->db->f('account_lid'), - 'account_type' => $this->db->f('account_type'), - 'account_firstname' => $this->db->f('account_firstname'), - 'account_lastname' => $this->db->f('account_lastname'), - 'account_status' => $this->db->f('account_status'), - 'account_expires' => $this->db->f('account_expires'), - 'person_id' => $this->db->f('person_id'), - 'account_primary_group' => $this->db->f('account_primary_group'), - 'account_email' => $this->db->f('account_email'), - ); - } - $this->db->query("SELECT count(*) FROM $this->table $whereclause"); - $this->total = $this->db->next_record() ? $this->db->f(0) : 0; - - return $accounts; - } - - /** - * converts a name / unique value from the accounts-table (account_lid,account_email) to an id - */ - function name2id($name,$which='account_lid') - { - $where = $which == 'account_fullname' ? '('.$this->db->concat('account_firstname',"' '",'account_lastname').')='.$this->db->quote($name) : - array($which => $name); - $this->db->select($this->table,'account_id,account_type',$where,__LINE__,__FILE__); - if($this->db->next_record()) - { - return ($this->db->f('account_type') == 'g' ? -1 : 1) * $this->db->f('account_id'); - } - return False; - } - - /** - * converts an id to the corresponding value of the accounts-table (account_lid,account_email,account_firstname,...) - */ - function id2name($account_id,$which='account_lid') - { - $this->db->select($this->table,$this->db->name_quote($which),array('account_id'=>abs($account_id)),__LINE__,__FILE__); - if($this->db->next_record()) - { - return $this->db->f(0); - } - return False; - } - - function exists($account_lid) - { - static $by_id, $by_lid; - - $where = array(); - if(is_numeric($account_lid)) - { - if(@isset($by_id[$account_lid]) && $by_id[$account_lid] != '') - { - return $by_id[$account_lid]; - } - $where['account_id'] = abs($account_lid); - } - else - { - if(@isset($by_lid[$account_lid]) && $by_lid[$account_lid] != '') - { - return $by_lid[$account_lid]; - } - $where['account_lid'] = $account_lid; - } - - $this->db->select($this->table,'count(*)',$where,__LINE__,__FILE__); - $this->db->next_record(); - $ret_val = $this->db->f(0) > 0; - if(is_numeric($account_lid)) - { - $by_id[$account_lid] = $ret_val; - $by_lid[$this->id2name($account_lid)] = $ret_val; - } - else - { - $by_lid[$account_lid] = $ret_val; - $by_id[$this->name2id($account_lid)] = $ret_val; - } - return $ret_val; - } - - function create($account_info) - { - $account_data = array( - 'account_lid' => $account_info['account_lid'], - 'account_pwd' => $GLOBALS['egw']->common->encrypt_password($account_info['account_passwd'],True), - 'account_firstname' => $account_info['account_firstname'], - 'account_lastname' => $account_info['account_lastname'], - 'account_status' => $account_info['account_status'], - 'account_expires' => $account_info['account_expires'], - 'account_type' => $account_info['account_type'], - 'person_id' => $account_info['person_id'], - 'account_primary_group' => $account_info['account_primary_group'], - 'account_email' => $account_info['account_email'], - ); - if (isset($account_info['account_id']) && (int)$account_info['account_id'] && !$this->id2name($account_info['account_id'])) - { - // only use account_id, if it's not already used - $account_data['account_id'] = abs($account_info['account_id']); - } - if (!$this->db->insert($this->table,$account_data,False,__LINE__,__FILE__)) - { - return false; - } - $id = $account_data['account_id'] ? $account_data['account_id'] : $this->db->get_last_insert_id($this->table,'account_id'); - - if ($account_info['account_type'] == 'g' && $id > 0) // create negative id for groups - { - $id = -$id; - } - return $id; - } - - function auto_add($accountname, $passwd, $default_prefs = False, $default_acls = False, $expiredate = 0, $account_status = 'A') - { - if ($expiredate == 0) - { - if(isset($GLOBALS['egw_info']['server']['auto_create_expire']) == True) - { - if($GLOBALS['egw_info']['server']['auto_create_expire'] == 'never') - { - $expires = -1; - } - else - { - $expiredate = time() + $GLOBALS['egw_info']['server']['auto_create_expire']; - } - } - } - else - { - /* expire in 30 days by default */ - $expiredate = time() + ((60 * 60) * (30 * 24)); - } - - if ($expires != -1) - { - $expires = mktime(2,0,0,date('n',$expiredate), (int)date('d',$expiredate), date('Y',$expiredate)); - } - - $default_group_id = $this->name2id($GLOBALS['egw_info']['server']['default_group_lid']); - if (!$default_group_id) - { - $default_group_id = (int) $this->name2id('Default'); - } - $primary_group = $GLOBALS['auto_create_acct']['primary_group'] && - $this->get_type((int)$GLOBALS['auto_create_acct']['primary_group']) == 'g' ? - (int) $GLOBALS['auto_create_acct']['primary_group'] : $default_group_id; - - $acct_info = array( - 'account_id' => (int) $GLOBALS['auto_create_acct']['id'], - 'account_lid' => $accountname, - 'account_type' => 'u', - 'account_passwd' => $passwd, - 'account_firstname' => $GLOBALS['auto_create_acct']['firstname'] ? $GLOBALS['auto_create_acct']['firstname'] : 'New', - 'account_lastname' => $GLOBALS['auto_create_acct']['lastname'] ? $GLOBALS['auto_create_acct']['lastname'] : 'User', - 'account_status' => $account_status, - 'account_expires' => $expires, - 'account_primary_group' => $primary_group, - ); - - /* attempt to set an email address */ - if (isset($GLOBALS['auto_create_acct']['email']) == True && $GLOBALS['auto_create_acct']['email'] != '') - { - $acct_info['account_email'] = $GLOBALS['auto_create_acct']['email']; - } - elseif(isset($GLOBALS['egw_info']['server']['mail_suffix']) == True && $GLOBALS['egw_info']['server']['mail_suffix'] != '') - { - $acct_info['account_email'] = $accountname . '@' . $GLOBALS['egw_info']['server']['mail_suffix']; - } - - $this->db->transaction_begin(); - - $accountid = $this->create($acct_info); /* create the account */ - - if ($accountid) /* begin account setup */ - { - /* If we have a primary_group, add it as "regular" eGW group (via ACL) too. */ - if ($primary_group) - { - $GLOBALS['egw']->acl->add_repository('phpgw_group', $primary_group,$accountid,1); - } - - /* if we have an mail address set it in the users' email preference */ - if (isset($GLOBALS['auto_create_acct']['email']) && $GLOBALS['auto_create_acct']['email'] != '') - { - $GLOBALS['egw']->acl->acl($accountid); /* needed als preferences::save_repository calls acl */ - $GLOBALS['egw']->preferences->preferences($accountid); - $GLOBALS['egw']->preferences->read_repository(); - $GLOBALS['egw']->preferences->add('email','address',$GLOBALS['auto_create_acct']['email']); - $GLOBALS['egw']->preferences->save_repository(); - } - /* use the default mail domain to set the uesrs' email preference */ - elseif(isset($GLOBALS['egw_info']['server']['mail_suffix']) && $GLOBALS['egw_info']['server']['mail_suffix'] != '') - { - $GLOBALS['egw']->acl->acl($accountid); /* needed als preferences::save_repository calls acl */ - $GLOBALS['egw']->preferences->preferences($accountid); - $GLOBALS['egw']->preferences->read_repository(); - $GLOBALS['egw']->preferences->add('email','address', $accountname . '@' . $GLOBALS['egw_info']['server']['mail_suffix']); - $GLOBALS['egw']->preferences->save_repository(); - } - - /* commit the new account transaction */ - $this->db->transaction_commit(); - - // call hook to notify interested apps about the new account - $GLOBALS['hook_values']['account_lid'] = $acct_info['account_lid']; - $GLOBALS['hook_values']['account_id'] = $accountid; - $GLOBALS['hook_values']['new_passwd'] = $acct_info['account_passwd']; - $GLOBALS['hook_values']['account_status'] = $acct_info['account_status']; - $GLOBALS['hook_values']['account_firstname'] = $acct_info['account_firstname']; - $GLOBALS['hook_values']['account_lastname'] = $acct_info['account_lastname']; - $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( - 'location' => 'addaccount', - // at login-time only the hooks from the following apps will be called - 'order' => array('felamimail','fudforum'), - ),False,True); /* called for every app now, not only enabled ones */ - - } /* end account setup */ - else /* if no account id abort the account creation */ - { - $this->db->transaction_abort(); - } - - /* - * If we succeeded in creating the account (above), return the accountid, else, - * return the error value from $this->name2id($accountname) - */ - return $accountid; - - } /* end auto_add() */ - - function get_account_name($accountid,&$lid,&$fname,&$lname) - { - $this->db->select($this->table,'account_lid,account_firstname,account_lastname',array('account_id'=>abs($accountid)),__LINE__,__FILE__); - if (!$this->db->next_record()) - { - return False; - } - $lid = $this->db->f('account_lid'); - $fname = $this->db->f('account_firstname'); - $lname = $this->db->f('account_lastname'); - - return True; - } + if (!(int)$account_id) return false; - /** - * Update the last login timestamps and the IP - * - * @param int $account_id - * @param string $ip - * @return int lastlogin time - */ - function update_lastlogin($account_id, $ip) + $this->db->select($this->table,'*',array('account_id' => abs($account_id)),__LINE__,__FILE__); + if (!($data = $this->db->row(true))) { - $this->db->select($this->table,'account_lastlogin',array('account_id'=>abs($account_id)),__LINE__,__FILE__); - $previous_login = $this->db->next_record() ? $this->db->f('account_lastlogin') : false; + return false; + } + if ($data['account_type'] == 'g') + { + $data['account_id'] = -$data['account_id']; + } + $data['account_fullname'] = $data['account_firstname'].' '.$data['account_lastname']; - $this->db->update($this->table,array( - 'account_lastloginfrom' => $ip, - 'account_lastlogin' => time(), - ),array( - 'account_id' => abs($account_id), - ),__LINE__,__FILE__); - - return $previous_login; + return $data; + } + + /** + * Saves / adds the data of one account + * + * If no account_id is set in data the account is added and the new id is set in $data. + * + * @param array $data array with account-data + * @return int/boolean the account_id or false on error + */ + function save(&$data) + { + echo "

accounts_sql::save(".print_r($data,true).")

\n"; + $to_write = $data; + unset($to_write['account_id']); + unset($to_write['account_passwd']); + + // encrypt password if given or unset it if not + if ($data['account_passwd']) + { + $to_write['account_pwd'] = $GLOBALS['egw']->auth->encrypt_sql($data['account_passwd']); + } + if (!(int)$data['account_id']) + { + if (!in_array($to_write['account_type'],array('u','g')) || + !$this->db->insert($this->table,$to_write,false,__LINE__,__FILE__)) return false; + + $data['account_id'] = $this->db->get_last_insert_id($this->table,'account_id'); + if ($data['account_type'] == 'g') $data['account_id'] *= -1; + } + elseif (!$this->db->update($this->table,$to_write,array('account_id' => abs($data['account_id'])),__LINE__,__FILE__)) + { + return false; + } + return $data['account_id']; + } + + /** + * Delete one account, deletes also all acl-entries for that account + * + * @param int $id numeric account_id + * @return boolean true on success, false otherwise + */ + function delete($account_id) + { + if (!(int)$account_id) return false; + + return !!$this->db->delete($this->table,array('account_id' => abs($account_id)),__LINE__,__FILE__); + } + + /** + * Get all memberships of an account $accountid / groups the account is a member off + * + * @param int $account_id numeric account-id + * @return array/boolean array with account_id => account_lid pairs or false if account not found + */ + function memberships($account_id) + { + if (!(int)$account_id) return false; + + $memberships = array(); + if(($gids = $GLOBALS['egw']->acl->get_location_list_for_id('phpgw_group', 1, $account_id))) + { + foreach($gids as $gid) + { + $memberships[(string) $gid] = $this->id2name($gid); + } + } + //echo "accounts::memberships($account_id)"; _debug_array($memberships); + return $memberships; + } + + /** + * Sets the memberships of the account this class is instanciated for + * + * @param array $groups array with gidnumbers + * @param int $account_id numerical account-id + */ + function set_memberships($groups,$account_id) + { + if (!(int)$account_id) return; + + $acl =& CreateObject('phpgwapi.acl',$account_id); + $acl->read_repository(); + $acl->delete('phpgw_group',false); + + foreach($groups as $group) + { + $acl->add('phpgw_group',$group,1); + } + $acl->save_repository(); + } + + /** + * Get all members of the group $accountid + * + * @param int/string $account_id numeric account-id + * @return array with account_id => account_lid pairs + */ + function members($account_id) + { + if (!($uids = $GLOBALS['egw']->acl->get_ids_for_location($account_id, 1, 'phpgw_group'))) + { + return False; + } + $members = array(); + foreach ($uids as $uid) + { + $members[$uid] = $this->id2name($uid); + } + //echo "accounts::members($accountid)"; _debug_array($members); + return $members; + } + + /** + * Set the members of a group + * + * @param array $members array with uidnumber or uid's + * @param int $gid gidnumber of group to set + */ + function set_members($members,$gid) + { + //echo "

accounts::set_members(".print_r($members,true).",$gid)

\n"; + $GLOBALS['egw']->acl->delete_repository('phpgw_group',$gid); + foreach($members as $id) + { + $GLOBALS['egw']->acl->add_repository('phpgw_group',$gid,$id,1); } } + + /** + * Searches users and/or groups + * + * ToDo: implement a search like accounts::search + * + * @param string $_type + * @param int $start=null + * @param string $sort='' + * @param string $order='' + * @param string $query + * @param int $offset=null + * @param string $query_type + * @return array + */ + function get_list($_type='both', $start = '',$sort = '', $order = '', $query = '', $offset = null, $query_type='') + { + if (! $sort) + { + $sort = "DESC"; + } + + if (!empty($order) && preg_match('/^[a-zA-Z_0-9, ]+$/',$order) && (empty($sort) || preg_match('/^(DESC|ASC|desc|asc)$/',$sort))) + { + $orderclause = "ORDER BY $order $sort"; + } + else + { + $orderclause = "ORDER BY account_lid ASC"; + } + + switch($_type) + { + case 'accounts': + $whereclause = "WHERE account_type = 'u'"; + break; + case 'groups': + $whereclause = "WHERE account_type = 'g'"; + break; + default: + $whereclause = ''; + } + + if ($query) + { + if ($whereclause) + { + $whereclause .= ' AND ( '; + } + else + { + $whereclause = ' WHERE ( '; + } + switch($query_type) + { + case 'all': + default: + $query = '%'.$query; + // fall-through + case 'start': + $query .= '%'; + // fall-through + case 'exact': + $query = $this->db->quote($query); + $whereclause .= " account_firstname LIKE $query OR account_lastname LIKE $query OR account_lid LIKE $query )"; + break; + case 'firstname': + case 'lastname': + case 'lid': + case 'email': + $query = $this->db->quote('%'.$query.'%'); + $whereclause .= " account_$query_type LIKE $query )"; + break; + } + } + + $sql = "SELECT * FROM $this->table $whereclause $orderclause"; + if ($offset) + { + $this->db->limit_query($sql,$start,__LINE__,__FILE__,$offset); + } + elseif (is_numeric($start)) + { + $this->db->limit_query($sql,$start,__LINE__,__FILE__); + } + else + { + $this->db->query($sql,__LINE__,__FILE__); + } + while ($this->db->next_record()) + { + $accounts[] = Array( + 'account_id' => ($this->db->f('account_type') == 'g' ? -1 : 1) * $this->db->f('account_id'), + 'account_lid' => $this->db->f('account_lid'), + 'account_type' => $this->db->f('account_type'), + 'account_firstname' => $this->db->f('account_firstname'), + 'account_lastname' => $this->db->f('account_lastname'), + 'account_status' => $this->db->f('account_status'), + 'account_expires' => $this->db->f('account_expires'), + 'person_id' => $this->db->f('person_id'), + 'account_primary_group' => $this->db->f('account_primary_group'), + 'account_email' => $this->db->f('account_email'), + ); + } + $this->db->query("SELECT count(*) FROM $this->table $whereclause"); + $this->total = $this->db->next_record() ? $this->db->f(0) : 0; + + return $accounts; + } + + /** + * convert an alphanumeric account-value (account_lid, account_email) to the account_id + * + * Please note: + * - if a group and an user have the same account_lid the group will be returned (LDAP only) + * - if multiple user have the same email address, the returned user is undefined + * + * @param string $name value to convert + * @param string $which='account_lid' type of $name: account_lid (default), account_email, person_id, account_fullname + * @param string $account_type u = user, g = group, default null = try both + * @return int/false numeric account_id or false on error ($name not found) + */ + function name2id($name,$which='account_lid',$account_type=null) + { + $where = array(); + switch($which) + { + case 'account_fullname': + $where[] = '('.$this->db->concat('account_firstname',"' '",'account_lastname').')='.$this->db->quote($name); + break; + + default: + $where[$which] = $name; + } + if ($account_type) + { + $where['account_type'] = $account_type; + } + $this->db->select($this->table,'account_id,account_type',$where,__LINE__,__FILE__); + if(!$this->db->next_record()) return false; + + return ($this->db->f('account_type') == 'g' ? -1 : 1) * $this->db->f('account_id'); + } + + /** + * Update the last login timestamps and the IP + * + * @param int $account_id + * @param string $ip + * @return int lastlogin time + */ + function update_lastlogin($account_id, $ip) + { + $this->db->select($this->table,'account_lastlogin',array('account_id'=>abs($account_id)),__LINE__,__FILE__); + $previous_login = $this->db->next_record() ? $this->db->f('account_lastlogin') : false; + + $this->db->update($this->table,array( + 'account_lastloginfrom' => $ip, + 'account_lastlogin' => time(), + ),array( + 'account_id' => abs($account_id), + ),__LINE__,__FILE__); + + return $previous_login; + } +} diff --git a/phpgwapi/inc/class.acl.inc.php b/phpgwapi/inc/class.acl.inc.php index 97f78e607c..8faa58b28b 100644 --- a/phpgwapi/inc/class.acl.inc.php +++ b/phpgwapi/inc/class.acl.inc.php @@ -151,8 +151,8 @@ { $this->acl(); } - $acl_acc_list = array_values((array)$this->get_location_list_for_id('phpgw_group', 1, $this->account_id)); - array_unshift($acl_acc_list,$this->account_id,0); + $acl_acc_list = $GLOBALS['egw']->accounts->memberships($this->account_id,true); + array_unshift($acl_acc_list,$this->account_id); $this->db->select($this->table_name,'*',array('acl_account' => $acl_acc_list ),__LINE__,__FILE__); $this->data = Array(); @@ -210,7 +210,7 @@ * Delete ACL record in the repository of the class * * @param string $appname appname or '' for $GLOBALS['egw_info']['flags']['currentapp'] - * @param string $location location + * @param string/boolean $location location or false for all locations * @return array all ACL records from $this->data. */ function delete($appname,$location) @@ -219,7 +219,9 @@ foreach($this->data as $idx => $value) { - if ($this->data[$idx]['appname'] == $appname && $this->data[$idx]['location'] == $location && $this->data[$idx]['account'] == $this->account_id) + if ($this->data[$idx]['appname'] == $appname && + ($location === false || $this->data[$idx]['location'] == $location) && + $this->data[$idx]['account'] == $this->account_id) { unset($this->data[$idx]); } diff --git a/phpgwapi/inc/class.auth.inc.php b/phpgwapi/inc/class.auth.inc.php index c4136db76f..e9828bbff7 100644 --- a/phpgwapi/inc/class.auth.inc.php +++ b/phpgwapi/inc/class.auth.inc.php @@ -165,8 +165,33 @@ } return $e_password; } + + /** + * Create an ldap hash from an sql hash + * + * @param string $hash + */ + function hash_sql2ldap($hash) + { + switch(strtolower($GLOBALS['egw_info']['server']['sql_encryption_type'])) + { + case '': // not set sql_encryption_type + case 'md5': + $hash = '{md5}' . base64_encode(pack("H*",$hash)); + break; + case 'crypt': + $hash = '{crypt}' . $hash; + break; + } + return $hash; + } - /* Create a password for storage in the accounts table */ + /** + * Create a password for storage in the accounts table + * + * @param string $password + * @return string hash + */ function encrypt_sql($password) { /* Grab configured type, or default to md5() (old method) */ @@ -249,7 +274,7 @@ * but as pecl dosn't run on any platform and isn't GPL'd * i haven't implemented it yet * Windows compatible check is: 7 char lenth, 1 Up, 1 Low, 1 Num and 1 Special - * @author cornelius weiss + * @author cornelius weiss * @return mixed false if password is considered "safe" or a string $message if "unsafe" */ function crackcheck($passwd) @@ -278,11 +303,11 @@ } /** - @function smd5_compare - @abstract compare SMD5-encrypted passwords for authentication - @param $form_val user input value for comparison - @param $db_val stored value (from database) - @return boolean True on successful comparison + * compare SMD5-encrypted passwords for authentication + * + * @param string $form_val user input value for comparison + * @param string $db_val stored value (from database) + * @return boolean True on successful comparison */ function smd5_compare($form_val,$db_val) { @@ -304,11 +329,11 @@ } /** - @function sha_compare - @abstract compare SHA-encrypted passwords for authentication - @param $form_val user input value for comparison - @param $db_val stored value (from database) - @return boolean True on successful comparison + * compare SHA-encrypted passwords for authentication + * + * @param string $form_val user input value for comparison + * @param string $db_val stored value (from database) + * @return boolean True on successful comparison */ function sha_compare($form_val,$db_val) { @@ -325,11 +350,11 @@ } /** - @function ssha_compare - @abstract compare SSHA-encrypted passwords for authentication - @param $form_val user input value for comparison - @param $db_val stored value (from database) - @return boolean True on successful comparison + * compare SSHA-encrypted passwords for authentication + * + * @param string $form_val user input value for comparison + * @param string $db_val stored value (from database) + * @return boolean True on successful comparison */ function ssha_compare($form_val,$db_val) { @@ -349,12 +374,12 @@ } /** - @function crypt_compare - @abstract compare crypted passwords for authentication whether des,ext_des,md5, or blowfish crypt - @param $form_val user input value for comparison - @param $db_val stored value (from database) - @param $type crypt() type - @return boolean True on successful comparison + * compare crypted passwords for authentication whether des,ext_des,md5, or blowfish crypt + * + * @param string $form_val user input value for comparison + * @param string $db_val stored value (from database) + * @param string $type crypt() type + * @return boolean True on successful comparison */ function crypt_compare($form_val,$db_val,$type) { @@ -378,13 +403,13 @@ } /** - @function md5_hmac_compare - @abstract compare md5_hmac-encrypted passwords for authentication (see RFC2104) - @param $form_val user input value for comparison - @param $db_val stored value (from database) - @param $key key for md5_hmac-encryption (username for imported smf users) - @return boolean True on successful comparison - */ + * compare md5_hmac-encrypted passwords for authentication (see RFC2104) + * + * @param string $form_val user input value for comparison + * @param string $db_val stored value (from database) + * @param string $key key for md5_hmac-encryption (username for imported smf users) + * @return boolean True on successful comparison + */ function md5_hmac_compare($form_val,$db_val,$key) { $key = str_pad(strlen($key) <= 64 ? $key : pack('H*', md5($key)), 64, chr(0x00)); diff --git a/phpgwapi/inc/class.auth_ldap.inc.php b/phpgwapi/inc/class.auth_ldap.inc.php index 5629d0658b..be220ac11b 100644 --- a/phpgwapi/inc/class.auth_ldap.inc.php +++ b/phpgwapi/inc/class.auth_ldap.inc.php @@ -42,6 +42,9 @@ { return False; } + // allow non-ascii in username & password + $username = $GLOBALS['egw']->translation->convert($username,$GLOBALS['egw']->translation->charset(),'utf-8'); + $passwd = $GLOBALS['egw']->translation->convert($passwd,$GLOBALS['egw']->translation->charset(),'utf-8'); if(!$ldap = @ldap_connect($GLOBALS['egw_info']['server']['ldap_host'])) { @@ -61,29 +64,30 @@ return False; } /* find the dn for this uid, the uid is not always in the dn */ - $attributes = array('uid','dn','givenName','sn','mail','uidNumber','gidNumber'); + $attributes = array('uid','dn','givenName','sn','mail','uidNumber','gidNumber','shadowExpire'); $filter = $GLOBALS['egw_info']['server']['ldap_search_filter'] ? $GLOBALS['egw_info']['server']['ldap_search_filter'] : '(uid=%user)'; $filter = str_replace(array('%user','%domain'),array($username,$GLOBALS['egw_info']['user']['domain']),$filter); if ($GLOBALS['egw_info']['server']['account_repository'] == 'ldap') { - $filter = "(&$filter(phpgwaccountstatus=A))"; + $filter = "(&$filter(objectclass=posixaccount))"; } - $sri = ldap_search($ldap, $GLOBALS['egw_info']['server']['ldap_context'], $filter, $attributes); $allValues = ldap_get_entries($ldap, $sri); if ($allValues['count'] > 0) { - if($GLOBALS['egw_info']['server']['case_sensitive_username'] == true) + if ($GLOBALS['egw_info']['server']['case_sensitive_username'] == true && + $allValues[0]['uid'][0] != $username) { - if($allValues[0]['uid'][0] != $username) - { - return false; - } + return false; + } + if ($GLOBALS['egw_info']['server']['account_repository'] == 'ldap' && + isset($allValues[0]['shawdowexpire']) && $allValues[0]['shawdowexpire'][0]*24*3600 < time()) + { + return false; // account is expired } - /* we only care about the first dn */ $userDN = $allValues[0]['dn']; /* generate a bogus password to pass if the user doesn't give us one @@ -93,12 +97,11 @@ { $passwd = crypt(microtime()); } - /* try to bind as the user with user suplied password */ + // try to bind as the user with user suplied password if (@ldap_bind($ldap, $userDN, $passwd)) { if ($GLOBALS['egw_info']['server']['account_repository'] != 'ldap') { - $account =& CreateObject('phpgwapi.accounts',$username,'u'); if (!$account->account_id && $GLOBALS['egw_info']['server']['auto_create_acct']) { // create a global array with all availible info about that account @@ -106,7 +109,7 @@ foreach(array( 'givenname' => 'firstname', 'sn' => 'lastname', - 'uidnumber' => 'id', + 'uidnumber' => 'account_id', 'mail' => 'email', 'gidnumber' => 'primary_group', ) as $ldap_name => $acct_name) @@ -116,19 +119,22 @@ } return True; } - $data = $account->read_repository(); - return $data['status'] == 'A'; + return ($id = $GLOBALS['egw']->accounts->name2id($username,'account_lid','u')) && + $GLOBALS['egw']->accounts->id2name($id,'account_status') == 'A'; } return True; } } - /* dn not found or password wrong */ + // dn not found or password wrong return False; } /** * changes password in LDAP * + * If $old_passwd is given, the password change is done binded as user and NOT with the + * "root" dn given in the configurations. + * * @param string $old_passwd must be cleartext or empty to not to be checked * @param string $new_passwd must be cleartext * @param int $account_id account id of user whose passwd should be changed @@ -142,7 +148,8 @@ } else { - $username = $GLOBALS['egw']->accounts->id2name($account_id); + $username = $GLOBALS['egw']->translation->convert($GLOBALS['egw']->accounts->id2name($account_id), + $GLOBALS['egw']->translation->charset(),'utf-8'); } //echo "

auth_ldap::change_password('$old_password','$new_passwd',$account_id) username='$username'

\n"; @@ -152,10 +159,14 @@ $ds = $GLOBALS['egw']->common->ldapConnect(); $sri = ldap_search($ds, $GLOBALS['egw_info']['server']['ldap_context'], $filter); $allValues = ldap_get_entries($ds, $sri); - + $entry['userpassword'] = $this->encrypt_password($new_passwd); $dn = $allValues[0]['dn']; + if($old_passwd) // if old password given (not called by admin) --> bind as that user to change the pw + { + $ds = $GLOBALS['egw']->common->ldapConnect('',$dn,$old_passwd); + } if (!@ldap_modify($ds, $dn, $entry)) { return false; diff --git a/phpgwapi/inc/class.sessions.inc.php b/phpgwapi/inc/class.sessions.inc.php index 2d18f715ff..3667e6f146 100644 --- a/phpgwapi/inc/class.sessions.inc.php +++ b/phpgwapi/inc/class.sessions.inc.php @@ -548,7 +548,7 @@ $GLOBALS['egw']->crypto->init(array($this->key,$this->iv)); $this->read_repositories(False); - if ($this->user['expires'] != -1 && $this->user['expires'] < time()) + if ($GLOBALS['egw']->accounts->is_expired($this->user)) { if(is_object($GLOBALS['egw']->log)) { diff --git a/phpgwapi/setup/setup.inc.php b/phpgwapi/setup/setup.inc.php index 86766479b6..a969f82296 100755 --- a/phpgwapi/setup/setup.inc.php +++ b/phpgwapi/setup/setup.inc.php @@ -14,7 +14,7 @@ /* Basic information about this app */ $setup_info['phpgwapi']['name'] = 'phpgwapi'; $setup_info['phpgwapi']['title'] = 'eGroupWare API'; - $setup_info['phpgwapi']['version'] = '1.3.006'; + $setup_info['phpgwapi']['version'] = '1.3.007'; $setup_info['phpgwapi']['versions']['current_header'] = '1.28'; $setup_info['phpgwapi']['enable'] = 3; $setup_info['phpgwapi']['app_order'] = 1; diff --git a/phpgwapi/setup/tables_update.inc.php b/phpgwapi/setup/tables_update.inc.php index 987fc98b1f..6340a0de3f 100644 --- a/phpgwapi/setup/tables_update.inc.php +++ b/phpgwapi/setup/tables_update.inc.php @@ -135,4 +135,87 @@ return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.3.006'; } -?> + + + $test[] = '1.3.006'; + function phpgwapi_upgrade1_3_006() + { + $GLOBALS['egw_setup']->db->select($GLOBALS['egw_setup']->config_table,'config_name,config_value',array( + 'config_app' => 'phpgwapi', + "(config_name LIKE '%ldap%' OR config_name IN ('auth_type','account_repository'))", + ),__LINE__,__FILE__); + while (($row = $GLOBALS['egw_setup']->db->row(true))) + { + $config[$row['config_name']] = $row['config_value']; + } + // the update is only for accounts in ldap + if ($config['account_repository'] == 'ldap' || !$config['account_repository'] && $config['auth_type'] == 'ldap') + { + $GLOBALS['egw_setup']->setup_account_object(); + if (!is_object($GLOBALS['egw']->acl)) + { + $GLOBALS['egw']->acl =& CreateObject('phpgwapi.acl'); + } + $ds = $GLOBALS['egw']->common->ldapConnect(); + $phpgwAccountAttributes = array( + 'phpgwaccounttype','phpgwaccountexpires','phpgwaccountstatus', + 'phpgwaccountlastlogin','phpgwaccountlastloginfrom','phpgwaccountlastpasswdchange', + ); + foreach(array($config['ldap_context'],$config['ldap_group_context']) as $context) + { + if (!$context) continue; + + $sri = ldap_search($ds,$context,'(objectclass=phpgwaccount)', + array_merge(array('gidnumber','objectclass'),$phpgwAccountAttributes)); + + foreach(ldap_get_entries($ds, $sri) as $key => $entry) + { + if ($key === 'count') continue; + + // remove the phpgwAccounts objectclass + $objectclass = $entry['objectclass']; + unset($objectclass['count']); + foreach($objectclass as $n => $class) $objectclass[$n] = strtolower($class); + unset($objectclass[array_search('phpgwaccount',$objectclass)]); + if ($entry['phpgwaccounttype'][0] == 'g') + { + if (!in_array('posixgroup',$objectclass)) $objectclass[] = 'posixgroup'; + $to_write = array('objectclass' => array_values($objectclass)); + // make sure all group-memberships are correctly set in LDAP + if (($uids = $GLOBALS['egw']->acl->get_ids_for_location($entry['gidnumber'][0],1,'phpgw_group'))) + { + foreach ($uids as $uid) + { + $to_write['memberuid'] = $GLOBALS['egw']->accounts->id2name($uid); + } + } + } + else // user + { + if (!in_array('posixaccount',$objectclass)) $objectclass[] = 'posixaccount'; + if (!in_array('shadowaccount',$objectclass)) $objectclass[] = 'shadowaccount'; + $to_write = array('objectclass' => array_values($objectclass)); + // store the important values of the phpgwaccount schema in the shadowAccount schema + if (!$entry['phpgwaccountstatus'][0] || $entry['phpgwaccountexpires'][0] != -1) + { + $to_write['shadowexpire'] = $entry['phpgwaccountexpires'][0] != -1 && + ($entry['phpgwaccountstatus'][0] || + !$entry['phpgwaccountstatus'][0] && $entry['phpgwaccountexpires'][0] < time()) ? + $entry['phpgwaccountexpires'][0] / (24*3600) : 0; + } + if ($entry['phpgwlastpasswdchange'][0]) + { + $to_write['shadowlastchange'] = $entry['phpgwlastpasswdchange'][0] / (24*3600); + } + } + foreach($phpgwAccountAttributes as $attr) + { + if (isset($entry[$attr])) $to_write[$attr] = array(); + } + echo $entry['dn']; _debug_array($to_write); + ldap_modify($ds,$entry['dn'],$to_write); + } + } + } + return $GLOBALS['setup_info']['phpgwapi']['currentver'] = '1.3.007'; + }