server_id = $server_id;
}
/**
* Check if there is sufficent information to Authenticate to the LDAP server.
*
* Given a server, returns whether or not we have enough information
* to authenticate against the server. For example, if the user specifies
* auth_type of 'cookie' in the config for that server, it checks the $_COOKIE array to
* see if the cookie username and password is set for the server. If the auth_type
* is 'session', the $_SESSION array is checked. If the auth_type is 'http', the
* $_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW'] is checked.
*
* There are three cases for this function depending on the auth_type configured for
* the specified server. If the auth_type is session or cookie or http, then getLoggedInDN()
* is called to verify that the user has logged in. If the auth_type is config, then the
* $ldapservers configuration in config.php is checked to ensure that the user has specified
* login information. In any case, if phpLDAPadmin has enough information to login
* to the server, true is returned. Otherwise false is returned.
*
* @return bool
* @see getLoggedInDN
*/
function haveAuthInfo() {
if (DEBUG_ENABLED) {
debug_log('Entered with ()',17,__FILE__,__LINE__,__METHOD__);
debug_log('We are a (%s) auth_type',80,__FILE__,__LINE__,__METHOD__,$this->auth_type);
}
# Set default return
$return = false;
# For session or cookie auth_types, we check the session or cookie to see if a user has logged in.
if (in_array($this->auth_type,array('session','cookie'))) {
/* we don't look at getLoggedInPass() cause it may be null for anonymous binds
* getLoggedInDN() will never return null if someone is really logged in. */
if ($this->getLoggedInDN())
$return = true;
else
$return = false;
/* whether or not the login_dn or pass is specified, we return
* true here. (if they are blank, we do an anonymous bind anyway) */
} elseif ($this->auth_type == 'http') {
# This is temp, to avoid multiple displays of this message
static $shown = false;
if (! $this->getLoggedInDN() && ! $shown) {
system_message(array(
'title'=>_('No HTTP AUTH information'),
'body'=>_('Your configuration file has authentication set to http_auth, however, there was none presented to phpLDAPadmin'),
'type'=>'error'));
$shown = true;
}
if ($this->getLoggedInDN())
$return = true;
else
$return = false;
} elseif ($this->auth_type == 'config') {
$return = true;
} else {
error(sprintf(_('Error: You have an error in your config file. The only three allowed values for auth_type in the $servers section are \'session\', \'cookie\', and \'config\'. You entered \'%s\', which is not allowed.'),htmlspecialchars($this->auth_type)),'error',null,true);
}
if (DEBUG_ENABLED)
debug_log('Returning (%s)',17,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Connect to the LDAP server.
*
* @param bool $process_error Whether to call an error page, if the connection fails
* @param bool $connect_id The ID for this connection
* @param bool $reconnect Use a cached connetion, or create a new one.
* @returns resource|false Connection resource to LDAP server, or false if no connection made.
*/
function connect($process_error=true,$connect_id='user',$reconnect=false,$readonly=true,$dn=null,$pass=null) {
global $CACHE;
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s,%s)',17,__FILE__,__LINE__,__METHOD__,
$process_error,$connect_id,$reconnect);
# Quick return if we have already connected.
if (isset($this->connection[$connect_id]['resource']) && ! $reconnect) {
if (DEBUG_ENABLED)
debug_log('Returning CACHED connection resource [%s](%s)',16,__FILE__,__LINE__,__METHOD__,
$this->connection[$connect_id]['resource'],$connect_id);
return $this->connection[$connect_id]['resource'];
}
# No identifiable connection exists, lest create a new one.
if (DEBUG_ENABLED)
debug_log('Creating new connection [%s] for Server ID [%s]',16,__FILE__,__LINE__,__METHOD__,
$connect_id,$this->server_id);
$this->connection[$connect_id]['resource'] = null;
# Grab the AUTH INFO based on the auth_type for this server
if ($connect_id == 'anonymous') {
if (DEBUG_ENABLED)
debug_log('This IS an anonymous login',80,__FILE__,__LINE__,__METHOD__);
$this->connection[$connect_id]['login_dn'] = null;
$this->connection[$connect_id]['login_pass'] = null;
} elseif ($this->auth_type == 'config') {
if (DEBUG_ENABLED)
debug_log('This IS a "config" login',80,__FILE__,__LINE__,__METHOD__);
if (! $this->login_dn) {
if (DEBUG_ENABLED)
debug_log('No login_dn for CONFIG auth_type, so well do anonymous',80,__FILE__,__LINE__,__METHOD__);
$connect_id = 'anonymous';
} else {
$this->connection[$connect_id]['login_dn'] = $this->login_dn;
$this->connection[$connect_id]['login_pass'] = $this->login_pass;
if (DEBUG_ENABLED)
debug_log('CONFIG auth_type settings, DN [%s], PASS [%s]',80,__FILE__,__LINE__,__METHOD__,
$this->connection[$connect_id]['login_dn'],
$this->connection[$connect_id]['login_pass'] ? md5($this->connection[$connect_id]['login_pass']) : '');
}
} elseif ($this->auth_type == 'http') {
if (! $dn && ! $pass)
$connect_id = 'anonymous';
else {
$this->connection[$connect_id]['login_dn'] = $dn;
$this->connection[$connect_id]['login_pass'] = $pass;
}
} else {
if (DEBUG_ENABLED)
debug_log('This IS some other login',80,__FILE__,__LINE__,__METHOD__);
# Did we pass the dn/pass to this function?
if ($dn) {
$this->connection[$connect_id]['login_dn'] = $dn;
$this->connection[$connect_id]['login_pass'] = $pass;
if (DEBUG_ENABLED)
debug_log('Login settings were passed to this function, DN [%s], PASS [%s]',80,__FILE__,__LINE__,__METHOD__,
$this->connection[$connect_id]['login_dn'],
$this->connection[$connect_id]['login_pass'] ? md5($this->connection[$connect_id]['login_pass']) : '');
# Was this an anonyous bind (the cookie stores 0 if so)?
} elseif ($this->getLoggedInDN() == 'anonymous') {
$connect_id = 'anonymous';
$this->connection[$connect_id]['login_dn'] = null;
$this->connection[$connect_id]['login_pass'] = null;
if (DEBUG_ENABLED)
debug_log('Already logged in as anonymous',80,__FILE__,__LINE__,__METHOD__);
} else {
$this->connection[$connect_id]['login_dn'] = $this->getLoggedInDN();
$this->connection[$connect_id]['login_pass'] = $this->getLoggedInPass();
if (DEBUG_ENABLED)
debug_log('Already logged in as DN [%s], PASS [%s]',80,__FILE__,__LINE__,__METHOD__,
$this->connection[$connect_id]['login_dn'],
$this->connection[$connect_id]['login_pass'] ? md5($this->connection[$connect_id]['login_pass']) : '');
}
}
# Work out if we are doing a SASL AUTH
if ($this->sasl_auth) {
$this->connection[$connect_id]['sasl_auth'] = true;
$this->connection[$connect_id]['sasl_mech'] = $this->sasl_mech;
$this->connection[$connect_id]['sasl_realm'] = $this->sasl_realm;
$this->connection[$connect_id]['sasl_authz_id'] = $this->sasl_authz_id;
$this->connection[$connect_id]['sasl_authz_id_regex'] = $this->sasl_authz_id_regex;
$this->connection[$connect_id]['sasl_authz_id_replacement'] = $this->sasl_authz_id_replacement;
$this->connection[$connect_id]['sasl_props'] = $this->sasl_props;
} else {
$this->connection[$connect_id]['sasl_auth'] = false;
}
if (DEBUG_ENABLED)
debug_log('Summary config settings, DN [%s], PASS [%s]',80,__FILE__,__LINE__,__METHOD__,
$this->connection[$connect_id]['login_dn'],
$this->connection[$connect_id]['login_pass'] ? md5($this->connection[$connect_id]['login_pass']) : '');
# Test if we have info to login.
if ($connect_id != 'anonymous' && ! $this->connection[$connect_id]['login_dn'] && ! $this->connection[$connect_id]['login_pass']) {
if (DEBUG_ENABLED)
debug_log('We dont have enough auth info for server [%s]',80,__FILE__,__LINE__,__METHOD__,$this->server_id);
return false;
}
# If we get here, we need to login - now figure out which server.
if (! $readonly) {
$host = $this->hostwr ? $this->hostwr : $this->host;
$port = $this->hostwr && $this->portwr ? $this->portwr : $this->port;
$connect_id = 'write';
} else {
$host = $this->host;
$port = $this->port;
}
# Our connect_id may have changed, lets just check and see if we have already connected.
if (isset($this->connection[$connect_id]['resource']) && ! $reconnect) {
if (DEBUG_ENABLED)
debug_log('Returning CACHED connection resource [%s](%s)',16,__FILE__,__LINE__,__METHOD__,
$this->connection[$connect_id]['resource'],$connect_id);
return $this->connection[$connect_id]['resource'];
}
run_hook('pre_connect',array('server_id'=>$this->server_id,'connect_id'=>$connect_id));
if ($port)
$resource = @ldap_connect($host,$port);
else
$resource = @ldap_connect($host);
if (DEBUG_ENABLED)
debug_log('LDAP Resource [%s], Host [%s], Port [%s]',16,__FILE__,__LINE__,__METHOD__,
$resource,$host,$port);
# Go with LDAP version 3 if possible (needed for renaming and Novell schema fetching)
@ldap_set_option($resource,LDAP_OPT_PROTOCOL_VERSION,3);
/* Disabling this makes it possible to browse the tree for Active Directory, and seems
* to not affect other LDAP servers (tested with OpenLDAP) as phpLDAPadmin explicitly
* specifies deref behavior for each ldap_search operation. */
@ldap_set_option($resource,LDAP_OPT_REFERRALS,0);
# Try to fire up TLS is specified in the config
if ($this->isTLSEnabled()) {
function_exists('ldap_start_tls') or error(_('Your PHP install does not support TLS.'),'error');
@ldap_start_tls($resource) or error(_('Could not start TLS. Please check your LDAP server configuration.'),'error',null,true);
}
$bind_result = false;
/**
* Implementation of SASL ldap_bind()
* This option requires PHP 5.x compiled with --with-ldap-sasl=DIR
*/
if (isset($this->connection[$connect_id]['sasl_auth']) &&
$this->connection[$connect_id]['sasl_auth'] == true) {
# No support for ldap_sasl_bind?
if (! function_exists('ldap_sasl_bind'))
error(_('Your PHP installation does not support ldap_sasl_bind() function. This function is present in PHP 5.x when compiled with --with-ldap-sasl.'),'error');
# Fill variables
$props = $this->connection[$connect_id]['sasl_props'];
$mech = $this->connection[$connect_id]['sasl_mech'];
$realm = $this->connection[$connect_id]['sasl_realm'];
$authz_id = null;
if (DEBUG_ENABLED)
debug_log('Resource [%s], Using SASL bind method. Bind DN [%s]',9,__FILE__,__LINE__,__METHOD__,
$resource,$this->connection[$connect_id]['login_dn']);
# do we need to rewrite authz_id?
if (isset($this->connection[$connect_id]['sasl_authz_id']) &&
strlen($this->connection[$connect_id]['sasl_authz_id']) > 0)
$authz_id = $this->connection[$connect_id]['sasl_authz_id'];
else {
# ok, here we go
if (DEBUG_ENABLED)
debug_log('Resource [%s], Rewriting bind DN [%s] -> authz_id with regex [%s] and replacement [%s].',9,__FILE__,__LINE__,__METHOD__,
$resource,$this->connection[$connect_id]['login_dn'],
$this->connection[$connect_id]['sasl_authz_id_regex'],
$this->connection[$connect_id]['sasl_authz_id_replacement']);
$authz_id = @preg_replace($this->connection[$connect_id]['sasl_authz_id_regex'],
$this->connection[$connect_id]['sasl_authz_id_replacement'],
$this->connection[$connect_id]['login_dn']);
# invalid regex?
if (is_null($authz_id)) {
error(sprintf(_('It seems that sasl_authz_id_regex "%s"." contains invalid PCRE regular expression.'),
$this->connection[$connect_id]['sasl_authz_id_regex']).((isset($php_errormsg)) ? ' Error message: '.$php_errormsg : '')
,'error','index.php');
}
}
if (DEBUG_ENABLED)
debug_log('Resource [%s], SASL OPTIONS: mech [%s], realm [%s], authz_id [%s], props [%s]',9,__FILE__,__LINE__,__METHOD__,
$resource,$mech,$realm,$authz_id,$props);
$bind_result = @ldap_sasl_bind($resource,
$this->connection[$connect_id]['login_dn'],$this->connection[$connect_id]['login_pass'],
$mech,$realm,$authz_id,$props);
} else {
$bind_result = @ldap_bind($resource,$this->connection[$connect_id]['login_dn'],
$this->connection[$connect_id]['login_pass']);
}
if (DEBUG_ENABLED)
debug_log('Resource [%s], Bind Result [%s]',16,__FILE__,__LINE__,__METHOD__,$resource,$bind_result);
if (! $bind_result) {
if (DEBUG_ENABLED)
debug_log('Leaving with FALSE, bind FAILed',16,__FILE__,__LINE__,__METHOD__);
if ($process_error) {
switch (ldap_errno($resource)) {
case 0x31:
error(_('Bad username or password. Please try again.'),'error');
break;
case 0x32:
error(_('Insufficient access rights.'),'error');
break;
case -1:
error(sprintf(_('Could not connect to "%s" on port "%s"'),$host,$port),'error');
break;
default:
error(_('Could not bind to the LDAP server (%s).',ldap_err2str($resource),$resource),'error');
}
} else {
return false;
}
}
if (is_resource($resource) && ($bind_result)) {
if (DEBUG_ENABLED)
debug_log('Bind successful',16,__FILE__,__LINE__,__METHOD__);
$this->connection[$connect_id]['resource'] = $resource;
}
if (DEBUG_ENABLED)
debug_log('Leaving with Connect [%s], Resource [%s]',16,__FILE__,__LINE__,__METHOD__,
$connect_id,$this->connection[$connect_id]['resource']);
return $this->connection[$connect_id]['resource'];
}
/**
* Gets the root DN of the specified LDAPServer, or null if it
* can't find it (ie, the server won't give it to us, or it isnt
* specified in the configuration file).
*
* Tested with OpenLDAP 2.0, Netscape iPlanet, and Novell eDirectory 8.7 (nldap.com)
* Please report any and all bugs!!
*
* Please note: On FC systems, it seems that php_ldap uses /etc/openldap/ldap.conf in
* the search base if it is blank - so edit that file and comment out the BASE line.
*
* @return array dn|null The root DN of the server on success (string) or null on error.
* @todo Sort the entries, so that they are in the correct DN order.
*/
function getBaseDN() {
if (DEBUG_ENABLED)
debug_log('Entered with ()',17,__FILE__,__LINE__,__METHOD__);
# Return the cached entry if we've been here before.
if (! is_null($this->_baseDN)) {
debug_log('Return CACHED BaseDN [%s]',17,__FILE__,__LINE__,__METHOD__,implode('|',$this->_baseDN));
return $this->_baseDN;
}
if (DEBUG_ENABLED)
debug_log('Checking config for BaseDN',80,__FILE__,__LINE__,__METHOD__);
# If the base is set in the configuration file, then just return that.
if (count($_SESSION[APPCONFIG]->ldapservers->GetValue($this->server_id,'server','base')) > 0) {
$this->_baseDN = $_SESSION[APPCONFIG]->ldapservers->GetValue($this->server_id,'server','base');
if (DEBUG_ENABLED)
debug_log('Return BaseDN from Config [%s]',17,__FILE__,__LINE__,__METHOD__,implode('|',$this->_baseDN));
return $this->_baseDN;
# We need to figure it out.
} else {
if (DEBUG_ENABLED)
debug_log('Connect to LDAP to find BaseDN',80,__FILE__,__LINE__,__METHOD__);
if ($this->connect()) {
$r = $this->search(null,'','objectClass=*',array('namingContexts'),'base');
$r = array_pop($r);
if (is_array($r))
$r = array_change_key_case($r);
if (isset($r['namingcontexts'])) {
if (! is_array($r['namingcontexts']))
$r['namingcontexts'] = array($r['namingcontexts']);
if (DEBUG_ENABLED)
debug_log('LDAP Entries:%s',80,__FILE__,__LINE__,__METHOD__,implode('|',$r['namingcontexts']));
$this->_baseDN = $r['namingcontexts'];
return $this->_baseDN;
} else {
return array('');
}
} else {
return array('');
}
}
}
/**
* Returns true if the specified server is configured to be displayed
* in read only mode.
*
* If a user has logged in via anonymous bind, and config.php specifies
*
* $config->custom->appearance['anonymous_bind_implies_read_only'] = true;
*
* then this also returns true. Servers can be configured read-only in
* config.php thus:
*
* $ldapservers->SetValue($i,'server','read_only',false);
*
*
* @return bool
*/
function isReadOnly() {
# Set default return
$return = false;
if ($this->read_only == true)
$return = true;
elseif ($this->getLoggedInDN() === 'anonymous' &&
($_SESSION[APPCONFIG]->GetValue('appearance','anonymous_bind_implies_read_only') === true))
$return = true;
if (DEBUG_ENABLED)
debug_log('Entered with (), Returning (%s)',17,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Returns true if the user has configured the specified server to enable mass deletion.
*
* Mass deletion is enabled in config.php this:
*
* $config->custom->commands['all'] = array('entry_delete' => array('mass_delete' => true));
*
* Notice that mass deletes are not enabled on a per-server basis, but this
* function checks that the server is not in a read-only state as well.
*
* @return bool
*/
function isMassDeleteEnabled() {
if (DEBUG_ENABLED)
debug_log('Entered with ()',17,__FILE__,__LINE__,__METHOD__);
if ($this->connect(false) && $this->haveAuthInfo() && ! $this->isReadOnly() &&
$_SESSION[APPCONFIG]->isCommandAvailable('entry_delete', 'mass_delete'))
return true;
else
return false;
}
/**
* Gets whether the admin has configured phpLDAPadmin to show the "Create New" link in the tree viewer.
*
* $ldapservers->SetValue($i,'appearance','show_create','true|false');
*
* If NOT set, then default to show the Create New item.
* If IS set, then return the value (it should be true or false).
*
* The entry creation command must be available.
*
* $config->custom->commands['all'] = array('entry_create' => true);
*
*
* @default true
* @return bool True if the feature is enabled and false otherwise.
*/
function isShowCreateEnabled() {
if (DEBUG_ENABLED)
debug_log('Entered with ()',17,__FILE__,__LINE__,__METHOD__);
if (! $_SESSION[APPCONFIG]->isCommandAvailable('entry_create')) return false;
else return $this->show_create;
}
/**
* Fetch whether the user has configured a certain server as "low bandwidth".
*
* Users may choose to configure a server as "low bandwidth" in config.php thus:
*
* $ldapservers->SetValue($i,'server','low_bandwidth','true|false');
*
*
* @default false
* @return bool
*/
function isLowBandwidth() {
if (DEBUG_ENABLED)
debug_log('Entered with ()',17,__FILE__,__LINE__,__METHOD__);
return $this->low_bandwidth;
}
/**
* Should this LDAP server be shown in the tree?
*
*
* $ldapservers->SetValue($i,'server','visible','true|false');
*
*
* @default true
* @return bool True if the feature is enabled and false otherwise.
*/
function isVisible() {
if ($this->visible)
$return = true;
else
$return = false;
if (DEBUG_ENABLED)
debug_log('Entered with (), Returning (%s)',17,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* This function will query the ldap server and request the subSchemaSubEntry which should be the Schema DN.
*
* If we cant connect to the LDAP server, we'll return false.
* If we can connect but cant get the entry, then we'll return null.
*
* @return array|false Schema if available, null if its not or false if we cant connect.
*/
function getSchemaDN($dn='') {
if (DEBUG_ENABLED)
debug_log('Entered with (%s)',25,__FILE__,__LINE__,__METHOD__,$dn);
# If we already got the SchemaDN, then return it.
if ($this->_schemaDN)
return $this->_schemaDN;
if (! $this->connect())
return false;
$this->lastop = 'read';
$search = @ldap_read($this->connect(),$dn,'objectClass=*',array('subschemaSubentry'));
if (DEBUG_ENABLED)
debug_log('Search returned (%s)',24,__FILE__,__LINE__,__METHOD__,is_resource($search));
# Fix for broken ldap.conf configuration.
if (! $search && ! $dn) {
if (DEBUG_ENABLED)
debug_log('Trying to find the DN for "broken" ldap.conf',80,__FILE__,__LINE__,__METHOD__);
if (isset($this->_baseDN)) {
foreach ($this->_baseDN as $base) {
$search = @ldap_read($this->connect(),$base,'objectClass=*',array('subschemaSubentry'));
if (DEBUG_ENABLED)
debug_log('Search returned (%s) for base (%s)',24,__FILE__,__LINE__,__METHOD__,
is_resource($search),$base);
if ($search)
break;
}
}
}
if (! $search)
return null;
if (! @ldap_count_entries($this->connect(),$search)) {
if (DEBUG_ENABLED)
debug_log('Search returned 0 entries. Returning NULL',25,__FILE__,__LINE__,__METHOD__);
return null;
}
$entries = @ldap_get_entries($this->connect(),$search);
if (DEBUG_ENABLED)
debug_log('Search returned [%s]',24,__FILE__,__LINE__,__METHOD__,$entries);
if (! $entries || ! is_array($entries))
return null;
$entry = isset($entries[0]) ? $entries[0] : false;
if (! $entry) {
if (DEBUG_ENABLED)
debug_log('Entry is false, Returning NULL',80,__FILE__,__LINE__,__METHOD__);
return null;
}
$sub_schema_sub_entry = isset($entry[0]) ? $entry[0] : false;
if (! $sub_schema_sub_entry) {
if (DEBUG_ENABLED)
debug_log('Sub Entry is false, Returning NULL',80,__FILE__,__LINE__,__METHOD__);
return null;
}
$this->_schemaDN = isset($entry[$sub_schema_sub_entry][0]) ? $entry[$sub_schema_sub_entry][0] : false;
if (DEBUG_ENABLED)
debug_log('Returning (%s)',25,__FILE__,__LINE__,__METHOD__,$this->_schemaDN);
return $this->_schemaDN;
}
/**
* Fetches the raw schema array for the subschemaSubentry of the server. Note,
* this function has grown many hairs to accomodate more LDAP servers. It is
* needfully complicated as it now supports many popular LDAP servers that
* don't necessarily expose their schema "the right way".
*
* Please note: On FC systems, it seems that php_ldap uses /etc/openldap/ldap.conf in
* the search base if it is blank - so edit that file and comment out the BASE line.
*
* @param string $schema_to_fetch - A string indicating which type of schema to
* fetch. Five valid values: 'objectclasses', 'attributetypes',
* 'ldapsyntaxes', 'matchingruleuse', or 'matchingrules'.
* Case insensitive.
* @param dn $dn (optional) This paremeter is the DN of the entry whose schema you
* would like to fetch. Entries have the option of specifying
* their own subschemaSubentry that points to the DN of the system
* schema entry which applies to this attribute. If unspecified,
* this will try to retrieve the schema from the RootDSE subschemaSubentry.
* Failing that, we use some commonly known schema DNs. Default
* value is the Root DSE DN (zero-length string)
* @return array an array of strings of this form:
* Array (
* [0] => "(1.3.6.1.4.1.7165.1.2.2.4 NAME 'gidPool' DESC 'Pool ...
* [1] => "(1.3.6.1.4.1.7165.2.2.3 NAME 'sambaAccount' DESC 'Sa ...
* etc.
*/
function getRawSchema($schema_to_fetch,$dn='') {
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s)',25,__FILE__,__LINE__,__METHOD__,$schema_to_fetch,$dn);
$valid_schema_to_fetch = array('objectclasses','attributetypes','ldapsyntaxes','matchingrules','matchingruleuse');
if (! $this->connect())
return false;
# error checking
$schema_to_fetch = strtolower($schema_to_fetch);
if (!is_null($this->_schema_entries) && isset($this->_schema_entries[$schema_to_fetch])) {
$schema = $this->_schema_entries[$schema_to_fetch];
if (DEBUG_ENABLED)
debug_log('Returning CACHED (%s)',25,__FILE__,__LINE__,__METHOD__,$schema);
return $schema;
}
# This error message is not localized as only developers should ever see it
if (! in_array($schema_to_fetch,$valid_schema_to_fetch))
error(sprintf('Bad parameter provided to function to %s::getRawSchema(). "%s" is not valid for the schema_to_fetch parameter.',
get_class($this),htmlspecialchars($schema_to_fetch)),'error','index.php');
# Try to get the schema DN from the specified entry.
$schema_dn = $this->getSchemaDN($dn);
# Do we need to try again with the Root DSE?
if (! $schema_dn)
$schema_dn = $this->getSchemaDN('');
# Store the eventual schema retrieval in $schema_search
$schema_search = null;
if ($schema_dn) {
if (DEBUG_ENABLED)
debug_log('Using Schema DN (%s)',24,__FILE__,__LINE__,__METHOD__,$schema_dn);
foreach (array('(objectClass=*)','(objectClass=subschema)') as $schema_filter) {
if (DEBUG_ENABLED)
debug_log('Looking for schema with Filter (%s)',24,__FILE__,__LINE__,__METHOD__,$schema_filter);
$schema_search = @ldap_read($this->connect(),$schema_dn,$schema_filter,array($schema_to_fetch),0,0,0,LDAP_DEREF_ALWAYS);
if (is_null($schema_search))
continue;
$schema_entries = @ldap_get_entries($this->connect(),$schema_search);
if (DEBUG_ENABLED)
debug_log('Search returned [%s]',24,__FILE__,__LINE__,__METHOD__,$schema_entries);
if (is_array($schema_entries) && isset($schema_entries['count']) && $schema_entries['count']) {
if (DEBUG_ENABLED)
debug_log('Found schema with (DN:%s) (FILTER:%s) (ATTR:%s)',24,__FILE__,__LINE__,__METHOD__,
$schema_dn,$schema_filter,$schema_to_fetch);
break;
}
if (DEBUG_ENABLED)
debug_log('Didnt find schema with filter (%s)',24,__FILE__,__LINE__,__METHOD__,$schema_filter);
unset($schema_entries);
$schema_search = null;
}
}
/* Second chance: If the DN or Root DSE didn't give us the subschemaSubentry, ie $schema_search
* is still null, use some common subSchemaSubentry DNs as a work-around. */
if (is_null($schema_search)) {
if (DEBUG_ENABLED)
debug_log('Attempting work-arounds for "broken" LDAP servers...',24,__FILE__,__LINE__,__METHOD__);
foreach ($this->getBaseDN() as $base) {
$ldap['W2K3 AD'][expand_dn_with_base($base,'cn=Aggregate,cn=Schema,cn=configuration,')] = '(objectClass=*)';
$ldap['W2K AD'][expand_dn_with_base($base,'cn=Schema,cn=configuration,')] = '(objectClass=*)';
$ldap['W2K AD'][expand_dn_with_base($base,'cn=Schema,ou=Admin,')] = '(objectClass=*)';
}
# OpenLDAP and Novell
$ldap['OpenLDAP']['cn=subschema'] = '(objectClass=*)';
foreach ($ldap as $ldap_server_name => $ldap_options) {
foreach ($ldap_options as $ldap_dn => $ldap_filter) {
if (DEBUG_ENABLED)
debug_log('Attempting [%s] (%s) (%s)
',24,__FILE__,__LINE__,__METHOD__,
$ldap_server_name,$ldap_dn,$ldap_filter);
$schema_search = @ldap_read($this->connect(),$ldap_dn,$ldap_filter,
array($schema_to_fetch), 0, 0, 0, LDAP_DEREF_ALWAYS);
if (is_null($schema_search))
continue;
$schema_entries = @ldap_get_entries($this->connect(),$schema_search);
if (DEBUG_ENABLED)
debug_log('Search returned [%s]',24,__FILE__,__LINE__,__METHOD__,$schema_entries);
if ($schema_entries && isset($schema_entries[0][$schema_to_fetch])) {
if (DEBUG_ENABLED)
debug_log('Found schema with filter of (%s)',24,__FILE__,__LINE__,__METHOD__,$ldap_filter);
break;
}
if (DEBUG_ENABLED)
debug_log('Didnt find schema with filter (%s)',24,__FILE__,__LINE__,__METHOD__,$ldap_filter);
unset($schema_entries);
$schema_search = null;
}
if ($schema_search)
break;
}
}
if (is_null($schema_search)) {
/* Still cant find the schema, try with the RootDSE
* Attempt to pull schema from Root DSE with scope "base", or
* Attempt to pull schema from Root DSE with scope "one" (work-around for Isode M-Vault X.500/LDAP) */
foreach (array('base','one') as $ldap_scope) {
if (DEBUG_ENABLED)
debug_log('Attempting to find schema with scope (%s), filter (objectClass=*) and a blank base.',24,__FILE__,__LINE__,__METHOD__,
$ldap_scope);
switch ($ldap_scope) {
case 'base':
$schema_search = @ldap_read($this->connect(),'','(objectClass=*)',array($schema_to_fetch),0,0,0,LDAP_DEREF_ALWAYS);
break;
case 'one':
$schema_search = @ldap_list($this->connect(),'','(objectClass=*)',array($schema_to_fetch),0,0,0,LDAP_DEREF_ALWAYS);
break;
}
if (is_null($schema_search))
continue;
$schema_entries = @ldap_get_entries($this->connect(),$schema_search);
if (DEBUG_ENABLED)
debug_log('Search returned [%s]',24,__FILE__,__LINE__,__METHOD__,$schema_entries);
if ($schema_entries && isset($schema_entries[0][$schema_to_fetch])) {
if (DEBUG_ENABLED)
debug_log('Found schema with filter of (%s)',24,__FILE__,__LINE__,__METHOD__,'(objectClass=*)');
break;
}
if (DEBUG_ENABLED)
debug_log('Didnt find schema with filter (%s)',24,__FILE__,__LINE__,__METHOD__,'(objectClass=*)');
unset($schema_entries);
$schema_search = null;
}
}
$schema_error_message = 'Please contact the phpLDAPadmin developers and let them know:
%s', $schema_to_fetch,gettype($schema_search),$schema_error_message,serialize($schema_search)),'error','index.php'); } else { $return = false; if (DEBUG_ENABLED) debug_log('Returning because schema_search type is not a resource (%s)',25,__FILE__,__LINE__,__METHOD__,$return); return $return; } } if (! $schema_entries) { $return = false; if (DEBUG_ENABLED) debug_log('Returning false since ldap_get_entries() returned false.',25,__FILE__,__LINE__,__METHOD__,$return); return $return; } if(! isset($schema_entries[0][$schema_to_fetch])) { if (in_array($schema_to_fetch,$schema_error_message_array)) { error(sprintf('Our attempts to find your SCHEMA for "%s" has return UNEXPECTED results.
%s', $schema_to_fetch,gettype($schema_search),$schema_error_message,serialize($schema_entries)),'error','index.php'); } else { $return = false; if (DEBUG_ENABLED) debug_log('Returning because (%s) isnt in the schema array. (%s)',25,__FILE__,__LINE__,__METHOD__,$schema_to_fetch,$return); return $return; } } /* Make a nice array of this form: Array ( [0] => "(1.3.6.1.4.1.7165.1.2.2.4 NAME 'gidPool' DESC 'Pool ...)" [1] => "(1.3.6.1.4.1.7165.2.2.3 NAME 'sambaAccount' DESC 'Sa ...)" etc.) */ $schema = $schema_entries[0][$schema_to_fetch]; unset($schema['count']); $this->_schema_entries[$schema_to_fetch] = $schema; if (DEBUG_ENABLED) debug_log('Returning (%s)',25,__FILE__,__LINE__,__METHOD__,$schema); return $schema; } /** * Return the attribute used for login */ function getLoginAttr() { return $this->login_attr; } /** * Fetches whether the login_attr feature is enabled for a specified server. * * This is configured in config.php thus: *
* $ldapservers->SetValue($i,'login','attr','');
*
*
* By virtue of the fact that the login_attr is not blank and not 'dn', the
* feature is configured to be enabled.
*
* @default uid
* @return bool
*/
function isLoginAttrEnabled() {
if ((strcasecmp($this->getLoginAttr(),'dn') != 0) && trim($this->getLoginAttr()))
$return = true;
else
$return = false;
if (DEBUG_ENABLED)
debug_log('Entered with (), Returning (%s)',17,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Fetches whether the login_attr feature is enabled for a specified server.
*
* This is configured in config.php thus:
*
* $ldapservers->SetValue($i,'login','attr','string');
*
*
* @return bool
*/
function isLoginStringEnabled() {
if (DEBUG_ENABLED)
debug_log('login_attr is [%s]',80,__FILE__,__LINE__,__METHOD__,$this->getLoginAttr());
if (! strcasecmp($this->getLoginAttr(),'string'))
$return = true;
else
$return = false;
if (DEBUG_ENABLED)
debug_log('Entered with (), Returning (%s)',17,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Fetches the login_attr string if enabled for a specified server.
*
* This is configured in config.php thus:
*
* $ldapservers->SetValue($i,'login','login_string','uid=,ou=People,dc=example,dc=com');
*
*
* @return string|false
*/
function getLoginString() {
if (DEBUG_ENABLED)
debug_log('Entered with ()',17,__FILE__,__LINE__,__METHOD__,__FILE__,__LINE__,__METHOD__);
return $this->login_string;
}
/**
* Fetch whether the user has configured a certain server login to be non anonymous
*
*
* $ldapservers->SetValue($i,'login','anon_bind','true|false');
*
*
* @default true
* @return bool
*/
function isAnonBindAllowed() {
# If only_login_allowed_dns is set, then we cant have anonymous.
if (count($_SESSION[APPCONFIG]->ldapservers->GetValue($this->server_id,'login','allowed_dns')) > 0)
$return = false;
else
$return = $_SESSION[APPCONFIG]->ldapservers->GetValue($this->server_id,'login','anon_bind');
if (DEBUG_ENABLED)
debug_log('Entered with (), Returning (%s)',17,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Fetches whether TLS has been configured for use with a certain server.
*
* Users may configure phpLDAPadmin to use TLS in config,php thus:
*
* $ldapservers->SetValue($i,'login','tls','true|false');
*
*
* @default false
* @return bool
*/
function isTLSEnabled() {
if (DEBUG_ENABLED)
debug_log('Entered with ()',17,__FILE__,__LINE__,__METHOD__);
return $this->tls;
}
/**
* Returns true if the user has configured the specified server to enable branch (non-leaf) renames.
*
* This is configured in config.php thus:
*
* $ldapservers->SetValue($i,'server','branch_rename','true|false');
*
*
* @default false
* @param int $server_id The ID of the server of interest from config.php.
* @return bool
*/
function isBranchRenameEnabled() {
debug_log('Entered with (), Returning (%s).',17,__FILE__,__LINE__,__METHOD__,$this->branch_rename);
return $this->branch_rename;
}
/**
* Gets an associative array of ObjectClass objects for the specified
* server. Each array entry's key is the name of the objectClass
* in lower-case and the value is an ObjectClass object.
*
* @param string $dn (optional) It is easier to fetch schema if a DN is provided
* which defines the subschemaSubEntry attribute (all entries should).
*
* @return array An array of ObjectClass objects.
*
* @see ObjectClass
* @see getSchemaObjectClass
*/
function SchemaObjectClasses($dn='') {
debug_log('Entered with (%s)',25,__FILE__,__LINE__,__METHOD__,$dn);
# Set default return
$return = null;
if ($return = get_cached_item($this->server_id,'schema','objectclasses')) {
debug_log('Returning CACHED [%s] (%s)',25,__FILE__,__LINE__,__METHOD__,$this->server_id,'objectclasses');
return $return;
}
$raw_oclasses = $this->getRawSchema('objectclasses',$dn);
if ($raw_oclasses) {
# build the array of objectClasses
$return = array();
foreach ($raw_oclasses as $class_string) {
if (is_null($class_string) || ! strlen($class_string))
continue;
$object_class = new ObjectClass($class_string,$this);
$return[strtolower($object_class->getName())] = $object_class;
}
# Now go through and reference the parent/child relationships
foreach ($return as $oclass) {
foreach ($oclass->getSupClasses() as $parent_name) {
if (isset($return[strtolower($parent_name)]))
$return[strtolower($parent_name)]->addChildObjectClass($oclass->getName());
}
}
ksort($return);
# cache the schema to prevent multiple schema fetches from LDAP server
set_cached_item($this->server_id,'schema','objectclasses',$return);
}
debug_log('Returning (%s)',25,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Gets a single ObjectClass object specified by name.
*
* @param string $oclass_name The name of the objectClass to fetch.
* @param string $dn (optional) It is easier to fetch schema if a DN is provided
* which defines the subschemaSubEntry attribute (all entries should).
*
* @return ObjectClass The specified ObjectClass object or false on error.
*
* @see ObjectClass
* @see SchemaObjectClasses
*/
function getSchemaObjectClass($oclass_name,$dn='') {
$oclass_name = strtolower($oclass_name);
$oclasses = $this->SchemaObjectClasses($dn);
# Default return value
$return = false;
if (isset($oclasses[$oclass_name]))
$return = $oclasses[$oclass_name];
debug_log('Entered with (%s,%s), Returning (%s).',25,__FILE__,__LINE__,__METHOD__,$oclass_name,$dn,$return);
return $return;
}
/**
* Gets a single AttributeType object specified by name.
*
* @param string $oclass_name The name of the AttributeType to fetch.
* @param string $dn (optional) It is easier to fetch schema if a DN is provided
* which defines the subschemaSubEntry attribute (all entries should).
*
* @return AttributeType The specified AttributeType object or false on error.
*
* @see AttributeType
* @see SchemaAttributes
*/
function getSchemaAttribute($attr_name,$dn=null) {
$attr_name = real_attr_name(strtolower($attr_name));
$schema_attrs = $this->SchemaAttributes($dn);
# Default return value
$return = false;
if (isset($schema_attrs[$attr_name]))
$return = $schema_attrs[$attr_name];
debug_log('Entered with (%s,%s), Returning (%s).',25,__FILE__,__LINE__,__METHOD__,$attr_name,$dn,$return);
return $return;
}
/**
* Gets an associative array of AttributeType objects for the specified
* server. Each array entry's key is the name of the attributeType
* in lower-case and the value is an AttributeType object.
*
* @param int $server_id The ID of the server whose AttributeTypes to fetch
* @param string $dn (optional) It is easier to fetch schema if a DN is provided
* which defines the subschemaSubEntry attribute (all entries should).
*
* @return array An array of AttributeType objects.
*/
function SchemaAttributes($dn=null) {
debug_log('Entered with (%s)',25,__FILE__,__LINE__,__METHOD__,$dn);
# Set default return
$return = null;
if ($return = get_cached_item($this->server_id,'schema','attributes')) {
debug_log('(): Returning CACHED [%s] (%s)',25,__FILE__,__LINE__,__METHOD__,$this->server_id,'attributes');
return $return;
}
$raw_attrs = $this->getRawSchema('attributeTypes',$dn);
if ($raw_attrs) {
# build the array of attribueTypes
$syntaxes = $this->SchemaSyntaxes($dn);
$attrs = array();
/**
* bug 856832: create two arrays - one indexed by name (the standard
* $attrs array above) and one indexed by oid (the new $attrs_oid array
* below). This will help for directory servers, like IBM's, that use OIDs
* in their attribute definitions of SUP, etc
*/
$attrs_oid = array();
foreach ($raw_attrs as $attr_string) {
if (is_null($attr_string) || ! strlen($attr_string))
continue;
$attr = new AttributeType($attr_string);
if (isset($syntaxes[$attr->getSyntaxOID()])) {
$syntax = $syntaxes[$attr->getSyntaxOID()];
$attr->setType($syntax->getDescription());
}
$attrs[strtolower($attr->getName())] = $attr;
/**
* bug 856832: create an entry in the $attrs_oid array too. This
* will be a ref to the $attrs entry for maintenance and performance
* reasons
*/
$attrs_oid[$attr->getOID()] = &$attrs[strtolower($attr->getName())];
}
# go back and add data from aliased attributeTypes
foreach ($attrs as $name => $attr) {
$aliases = $attr->getAliases();
if (is_array($aliases) && count($aliases) > 0) {
/* foreach of the attribute's aliases, create a new entry in the attrs array
* with its name set to the alias name, and all other data copied.*/
foreach ($aliases as $alias_attr_name) {
$new_attr = clone $attr;
$new_attr->setName($alias_attr_name);
$new_attr->addAlias($attr->getName());
$new_attr->removeAlias($alias_attr_name);
$new_attr_key = strtolower($alias_attr_name);
$attrs[$new_attr_key] = $new_attr;
}
}
}
# go back and add any inherited descriptions from parent attributes (ie, cn inherits name)
foreach ($attrs as $key => $attr) {
$sup_attr_name = $attr->getSupAttribute();
$sup_attr = null;
if (trim($sup_attr_name)) {
/* This loop really should traverse infinite levels of inheritance (SUP) for attributeTypes,
* but just in case we get carried away, stop at 100. This shouldn't happen, but for
* some weird reason, we have had someone report that it has happened. Oh well.*/
$i = 0;
while ($i++<100 /** 100 == INFINITY ;) */) {
if (isset($attrs_oid[$sup_attr_name])) {
$attr->setSupAttribute($attrs_oid[$sup_attr_name]->getName());
$sup_attr_name = $attr->getSupAttribute();
}
if (! isset($attrs[strtolower($sup_attr_name)])){
error(sprintf('Schema error: attributeType "%s" inherits from "%s", but attributeType "%s" does not exist.',
$attr->getName(),$sup_attr_name,$sup_attr_name),'error','index.php');
return;
}
$sup_attr = $attrs[strtolower($sup_attr_name)];
$sup_attr_name = $sup_attr->getSupAttribute();
# Does this superior attributeType not have a superior attributeType?
if (is_null($sup_attr_name) || strlen(trim($sup_attr_name)) == 0) {
/* Since this attribute's superior attribute does not have another superior
* attribute, clone its properties for this attribute. Then, replace
* those cloned values with those that can be explicitly set by the child
* attribute attr). Save those few properties which the child can set here:*/
$tmp_name = $attr->getName();
$tmp_oid = $attr->getOID();
$tmp_sup = $attr->getSupAttribute();
$tmp_aliases = $attr->getAliases();
$tmp_single_val = $attr->getIsSingleValue();
$tmp_desc = $attr->getDescription();
/* clone the SUP attributeType and populate those values
* that were set by the child attributeType */
$attr = clone $sup_attr;
$attr->setOID($tmp_oid);
$attr->setName($tmp_name);
$attr->setSupAttribute($tmp_sup);
$attr->setAliases($tmp_aliases);
$attr->setDescription($tmp_desc);
/* only overwrite the SINGLE-VALUE property if the child explicitly sets it
* (note: All LDAP attributes default to multi-value if not explicitly set SINGLE-VALUE) */
if ($tmp_single_val)
$attr->setIsSingleValue(true);
/* replace this attribute in the attrs array now that we have populated
new values therein */
$attrs[$key] = $attr;
# very important: break out after we are done with this attribute
$sup_attr_name = null;
$sup_attr = null;
break;
}
}
}
}
ksort($attrs);
# Add the used in and required_by values.
$schema_object_classes = $this->SchemaObjectClasses();
if (! is_array($schema_object_classes))
return array();
foreach ($schema_object_classes as $object_class) {
$must_attrs = $object_class->getMustAttrNames($schema_object_classes);
$may_attrs = $object_class->getMayAttrNames($schema_object_classes);
$oclass_attrs = array_unique(array_merge($must_attrs,$may_attrs));
# Add Used In.
foreach ($oclass_attrs as $attr_name) {
if (isset($attrs[strtolower($attr_name)]))
$attrs[strtolower($attr_name)]->addUsedInObjectClass($object_class->getName());
else {
#echo "Warning, attr not set: $attr_name
* dc=example,dc=com
* ou=People
* cn=Dave
* cn=Fred
* cn=Joe
* ou=More People
* cn=Mark
* cn=Bob
*
*
* Calling getContainerContents("ou=people,dc=example,dc=com")
* would return the following list:
*
*
* cn=Dave
* cn=Fred
* cn=Joe
* ou=More People
*
*
* @param string $dn The DN of the entry whose children to return.
* @param int $size_limit (optional) The maximum number of entries to return.
* If unspecified, no limit is applied to the number of entries in the returned.
* @param string $filter (optional) An LDAP filter to apply when fetching children, example: "(objectClass=inetOrgPerson)"
* @return array An array of DN strings listing the immediate children of the specified entry.
*/
function getContainerContents($dn,$size_limit=0,$filter='(objectClass=*)',$deref=LDAP_DEREF_ALWAYS) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s,%s,%s)',17,__FILE__,__LINE__,__METHOD__,$dn,$size_limit,$filter,$deref);
$return = array();
$search = $this->search(null,dn_escape($dn),$filter,array('dn'),'one',true,$deref,/*($size_limit > 0 ? $size_limit+1 : $size_limit)*/$size_limit);
if ($search) {
foreach ($search as $searchdn => $entry) {
$child_dn = $entry['dn'];
$return[] = $child_dn;
}
}
if (DEBUG_ENABLED)
debug_log('Returning (%s)',17,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* A handy ldap searching function very similar to PHP's ldap_search() with the
* following exceptions: Callers may specify a search scope and the return value
* is an array containing the search results rather than an LDAP result resource.
*
* Example usage:
*
* $samba_users = $ldapserver->search(null,"ou=People,dc=example,dc=com",
* "(&(objectClass=sambaAccount)(objectClass=posixAccount))",
* array("uid","homeDirectory"));
* print_r( $samba_users );
*
* // prints (for example):
* // Array (
* // [uid=jsmith,ou=People,dc=example,dc=com] => Array (
* // [dn] => "uid=jsmith,ou=People,dc=example,dc=com"
* // [uid] => "jsmith"
* // [homeDirectory] => "\\server\jsmith"
* // )
* // [uid=byoung,ou=People,dc=example,dc=com] => Array (
* // [dn] => "uid=byoung,ou=Samba,ou=People,dc=example,dc=com"
* // [uid] => "byoung"
* // [homeDirectory] => "\\server\byoung"
* // )
* // )
*
*
* WARNING: This function will use a lot of memory on large searches since the entire result set is
* stored in a single array. For large searches, you should consider sing the less memory intensive
* PHP LDAP API directly (ldap_search(), ldap_next_entry(), ldap_next_attribute(), etc).
*
* @param resource $resource If an existing LDAP results should be used.
* @param string $filter The LDAP filter to use when searching (example: "(objectClass=*)") (see RFC 2254)
* @param string $base_dn The DN of the base of search.
* @param array $attrs An array of attributes to include in the search result (example: array( "objectClass", "uid", "sn" )).
* @param string $scope The LDAP search scope. Must be one of "base", "one", or "sub". Standard LDAP search scope.
* @param bool $sort_results Specify false to not sort results by DN
* or true to have the returned array sorted by DN (uses ksort)
* or an array of attribute names to sort by attribute values
* @param int $deref When handling aliases or referrals, this specifies whether to follow referrals. Must be one of
* LDAP_DEREF_ALWAYS, LDAP_DEREF_NEVER, LDAP_DEREF_SEARCHING, or LDAP_DEREF_FINDING. See the PHP LDAP API for details.
* @param int $size_limit Size limit for search
*/
function search($resource=null,$base_dn=null,$filter,$attrs=array(),$scope='sub',$sort_results=true,$deref=LDAP_DEREF_NEVER,$size_limit=0) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s,%s,%s,%s,%s,%s)',17,__FILE__,__LINE__,__METHOD__,
is_resource($this),$base_dn,$filter,$attrs,$scope,$sort_results,$deref);
$this->lastop = 'read';
# If we dont have a resource, we'll connect with default settings
if (! is_resource($resource))
$resource = $this->connect(false);
# If the baseDN is null, we'll just search the first DN.
if (is_null($base_dn))
foreach ($this->getBaseDN() as $baseDN) {
$base_dn = $baseDN;
break;
}
if (DEBUG_ENABLED)
debug_log('%s search PREPARE.',16,__FILE__,__LINE__,__METHOD__,$scope);
switch ($scope) {
case 'base':
$search = @ldap_read($resource,$base_dn,$filter,$attrs,0,$size_limit,0,$deref);
break;
case 'one':
$search = @ldap_list($resource,$base_dn,$filter,$attrs,0,$size_limit,0,$deref);
break;
case 'sub':
default:
$search = @ldap_search($resource,$base_dn,$filter,$attrs,0,$size_limit,0,$deref);
break;
}
if (DEBUG_ENABLED)
debug_log('Search scope [%s] base [%s] filter [%s] attrs [%s] COMPLETE (%s).',16,__FILE__,__LINE__,__METHOD__,
$scope,$base_dn,$filter,$attrs,is_null($search));
if (! $search)
return array();
$return = array();
# @todo: this needs to convert everything to lowercase to work.
if (is_array($sort_results))
# we sort with the more important attribute (the first one) at the end
for ($i = count($sort_results) - 1; $i >= 0; --$i)
if (($sort_results[$i] == 'dn') || in_array($sort_results[$i], $attrs))
ldap_sort($resource, $search, $sort_results[$i]);
# Get the first entry identifier
if ($entry_id = ldap_first_entry($resource,$search)) {
# Iterate over the entries
while ($entry_id) {
# Get the distinguished name of the entry
$dn = ldap_get_dn($resource,$entry_id);
if (DEBUG_ENABLED)
debug_log('Got DN [%s].',64,__FILE__,__LINE__,__METHOD__,$dn);
$return[$dn]['dn'] = $dn;
# Get the attributes of the entry
$attrs = ldap_get_attributes($resource,$entry_id);
if (DEBUG_ENABLED)
debug_log('Got ATTRS [%s].',64,__FILE__,__LINE__,__METHOD__,$attrs);
# Get the first attribute of the entry
if ($attr = ldap_first_attribute($resource,$entry_id,$attrs)) {
if (DEBUG_ENABLED)
debug_log('Processing First ATTR [%s].',64,__FILE__,__LINE__,__METHOD__,$attr);
# Iterate over the attributes
while ($attr) {
if ($this->isAttrBinary($attr))
$values = ldap_get_values_len($resource,$entry_id,$attr);
else
$values = ldap_get_values($resource,$entry_id,$attr);
# Get the number of values for this attribute
$count = $values['count'];
unset($values['count']);
if ($count == 1)
$return[$dn][$attr] = $values[0];
else
$return[$dn][$attr] = $values;
$attr = ldap_next_attribute($resource,$entry_id,$attrs);
if (DEBUG_ENABLED)
debug_log('Processing Next ATTR [%s].',64,__FILE__,__LINE__,__METHOD__,$attr);
} # end while attr
}
$entry_id = ldap_next_entry($resource,$entry_id);
} # End while entry_id
}
if (($sort_results === true) && is_array($return))
ksort($return);
if (DEBUG_ENABLED)
debug_log('Returning (%s)',17,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Determines if an attribute's value can contain multiple lines. Attributes that fall
* in this multi-line category may be configured in config.php. Hence, this function
* accesses the global variable $_SESSION[APPCONFIG]->custom->appearance['multi_line_attributes'];
*
* Usage example:
*
* if ($ldapserver->isMultiLineAttr('postalAddress'))
* echo "";
* else
* echo "";
*
*
* @param string $attr_name The name of the attribute of interestd (case insensivite)
* @param string $val (optional) The current value of the attribute (speeds up the
* process by searching for carriage returns already in the attribute value)
* @return bool
*/
function isMultiLineAttr($attr_name,$val=null) {
# Set default return
$return = false;
# First, check the optional val param for a \n or a \r
if (! is_null($val) && (strpos($val,"\n") || strpos($val,"\r")))
$return = true;
# Next, compare strictly by name first
else
foreach ($_SESSION[APPCONFIG]->GetValue('appearance','multi_line_attributes') as $multi_line_attr_name)
if (strcasecmp($multi_line_attr_name,$attr_name) == 0) {
$return = true;
break;
}
# If unfound, compare by syntax OID
if (! $return) {
$schema_attr = $this->getSchemaAttribute($attr_name);
if ($schema_attr) {
$syntax_oid = $schema_attr->getSyntaxOID();
if ($syntax_oid)
foreach ($_SESSION[APPCONFIG]->GetValue('appearance','multi_line_syntax_oids') as $multi_line_syntax_oid)
if ($multi_line_syntax_oid == $syntax_oid) {
$return = true;
break;
}
}
}
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s), Returning (%s)',17,__FILE__,__LINE__,__METHOD__,$attr_name,$val,$return);
return $return;
}
/**
* Returns true if the attribute specified is required to take as input a DN.
* Some examples include 'distinguishedName', 'member' and 'uniqueMember'.
* @param string $attr_name The name of the attribute of interest (case insensitive)
* @return bool
*/
function isDNAttr($attr_name) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s)',17,__FILE__,__LINE__,__METHOD__,$attr_name);
# Simple test first
$dn_attrs = array('aliasedObjectName');
foreach ($dn_attrs as $dn_attr)
if (strcasecmp($attr_name,$dn_attr) == 0)
return true;
# Now look at the schema OID
$attr_schema = $this->getSchemaAttribute($attr_name);
if (! $attr_schema)
return false;
$syntax_oid = $attr_schema->getSyntaxOID();
if ('1.3.6.1.4.1.1466.115.121.1.12' == $syntax_oid)
return true;
if ('1.3.6.1.4.1.1466.115.121.1.34' == $syntax_oid)
return true;
$syntaxes = $this->SchemaSyntaxes();
if (! isset($syntaxes[$syntax_oid]))
return false;
$syntax_desc = $syntaxes[ $syntax_oid ]->getDescription();
if (strpos(strtolower($syntax_desc),'distinguished name'))
return true;
return false;
}
/**
* Responsible for setting two cookies/session-vars to indicate that a user has logged in,
* one for the logged in DN and one for the logged in password.
*
* This function is only used if 'auth_type' is set to 'cookie' or 'session'. The values
* written have the name "pla_login_dn_X" and "pla_login_pass_X" where X is the
* ID of the server to which the user is attempting login.
*
* Note that as with all cookie/session operations this function must be called BEFORE
* any output is sent to the browser.
*
* On success, true is returned. On failure, false is returned.
*
* @param string $dn The DN with which the user has logged in.
* @param string $password The password of the user logged in.
* @param bool $anon_bind Indicates that this is an anonymous bind such that
* a password of "0" is stored.
* @return bool
* @see unsetLoginDN
*/
function setLoginDN($dn,$password,$anon_bind) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s,%s)',17,__FILE__,__LINE__,__METHOD__,$dn,$password,$anon_bind);
if (! $this->auth_type)
return false;
switch ($this->auth_type) {
case 'cookie':
$cookie_dn_name = sprintf('pla_login_dn_%s',$this->server_id);
$cookie_pass_name = sprintf('pla_login_pass_%s',$this->server_id);
# we set the cookie password to 0 for anonymous binds.
if ($anon_bind) {
$dn = 'anonymous';
$password = '0';
}
$res1 = pla_set_cookie($cookie_dn_name,pla_blowfish_encrypt($dn));
$res2 = pla_set_cookie($cookie_pass_name,pla_blowfish_encrypt($password));
if ($res1 && $res2)
return true;
else
return false;
break;
case 'session':
$sess_var_dn_name = sprintf('pla_login_dn_%s',$this->server_id);
$sess_var_pass_name = sprintf('pla_login_pass_%s',$this->server_id);
# we set the cookie password to 0 for anonymous binds.
if ($anon_bind) {
$dn = 'anonymous';
$password = '0';
}
$_SESSION[$sess_var_dn_name] = pla_blowfish_encrypt($dn);
$_SESSION[$sess_var_pass_name] = pla_blowfish_encrypt($password);
return true;
break;
default:
error(sprintf(_('Unknown auth_type: %s'),htmlspecialchars($this->auth_type)),'error','index.php');
break;
}
}
/**
* Log a user out of the LDAP server.
*
* Removes the cookies/session-vars set by setLoginDN()
* after a user logs out using "auth_type" of "session" or "cookie".
* Returns true on success, false on failure.
*
* @return bool True on success, false on failure.
* @see setLoginDN
*/
function unsetLoginDN() {
if (DEBUG_ENABLED)
debug_log('Entered with ()',17,__FILE__,__LINE__,__METHOD__);
if (! $this->auth_type)
return false;
switch ($this->auth_type) {
case 'cookie':
$logged_in_dn = $this->getLoggedInDN();
if (! $logged_in_dn)
return false;
$logged_in_pass = $this->getLoggedInPass();
$anon_bind = $logged_in_dn == 'anonymous' ? true : false;
# set cookie with expire time already passed to erase cookie from client
$expire = time()-3600;
$cookie_dn_name = sprintf('pla_login_dn_%s',$this->server_id);
$cookie_pass_name = sprintf('pla_login_pass_%s',$this->server_id);
if ($anon_bind) {
$res1 = pla_set_cookie($cookie_dn_name,'anonymous',$expire);
$res2 = pla_set_cookie($cookie_pass_name,'0',$expire);
} else {
$res1 = pla_set_cookie($cookie_dn_name,pla_blowfish_encrypt($logged_in_dn),$expire);
$res2 = pla_set_cookie($cookie_pass_name,pla_blowfish_encrypt($logged_in_pass),$expire);
}
# Need to unset the cookies too, since they are still set if further processing occurs (eg: Timeout)
unset($_COOKIE[$cookie_dn_name]);
unset($_COOKIE[$cookie_pass_name]);
if (! $res1 || ! $res2)
return false;
else
return true;
break;
case 'session':
# unset session variables
$session_var_dn_name = sprintf('pla_login_dn_%s',$this->server_id);
$session_var_pass_name = sprintf('pla_login_pass_%s',$this->server_id);
if (array_key_exists($session_var_dn_name,$_SESSION))
unset($_SESSION[$session_var_dn_name]);
if (array_key_exists($session_var_pass_name,$_SESSION))
unset($_SESSION[$session_var_pass_name]);
return true;
break;
default:
error(sprintf(_('Unknown auth_type: %s'),htmlspecialchars($auth_type)),'error','index.php');
break;
}
}
/**
* Used to determine if the specified attribute is indeed a jpegPhoto. If the
* specified attribute is one that houses jpeg data, true is returned. Otherwise
* this function returns false.
*
* @param string $attr_name The name of the attribute to test.
* @return bool
* @see draw_jpeg_photos
*/
function isJpegPhoto($attr_name) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s)',17,__FILE__,__LINE__,__METHOD__,$attr_name);
# easy quick check
if (! strcasecmp($attr_name,'jpegPhoto') || ! strcasecmp($attr_name,'photo'))
return true;
# go to the schema and get the Syntax OID
$schema_attr = $this->getSchemaAttribute($attr_name);
if (! $schema_attr)
return false;
$oid = $schema_attr->getSyntaxOID();
$type = $schema_attr->getType();
if (! strcasecmp($type,'JPEG') || ($oid == '1.3.6.1.4.1.1466.115.121.1.28'))
return true;
return false;
}
/**
* Given an attribute name and server ID number, this function returns
* whether the attrbiute contains boolean data. This is useful for
* developers who wish to display the contents of a boolean attribute
* with a drop-down.
*
* @param string $attr_name The name of the attribute to test.
* @return bool
*/
function isAttrBoolean($attr_name) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s)',17,__FILE__,__LINE__,__METHOD__,$attr_name);
$type = ($schema_attr = $this->getSchemaAttribute($attr_name)) ? $schema_attr->getType() : null;
if (! strcasecmp('boolean',$type ) ||
! strcasecmp('isCriticalSystemObject',$attr_name) ||
! strcasecmp('showInAdvancedViewOnly',$attr_name))
return true;
else
return false;
}
/**
* Given an attribute name and server ID number, this function returns
* whether the attrbiute may contain binary data. This is useful for
* developers who wish to display the contents of an arbitrary attribute
* but don't want to dump binary data on the page.
*
* @param string $attr_name The name of the attribute to test.
* @return bool
*
* @see isJpegPhoto
*/
function isAttrBinary($attr_name) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s)',17,__FILE__,__LINE__,__METHOD__,$attr_name);
/**
* Determining if an attribute is binary can be an expensive operation.
* We cache the results for each attr name on each server in the $attr_cache
* to speed up subsequent calls. The $attr_cache looks like this:
*
* Array
* 0 => Array
* 'objectclass' => false
* 'cn' => false
* 'usercertificate' => true
* 1 => Array
* 'jpegphoto' => true
* 'cn' => false
*/
static $attr_cache;
$attr_name = strtolower($attr_name);
if (isset($attr_cache[$this->server_id][$attr_name]))
return $attr_cache[$this->server_id][$attr_name];
if ($attr_name == 'userpassword') {
$attr_cache[$this->server_id][$attr_name] = false;
return false;
}
# Quick check: If the attr name ends in ";binary", then it's binary.
if (strcasecmp(substr($attr_name,strlen($attr_name) - 7),';binary') == 0) {
$attr_cache[$this->server_id][$attr_name] = true;
return true;
}
# See what the server schema says about this attribute
$schema_attr = $this->getSchemaAttribute($attr_name);
if (! is_object($schema_attr)) {
/* Strangely, some attributeTypes may not show up in the server
* schema. This behavior has been observed in MS Active Directory.*/
$type = null;
$syntax = null;
} else {
$type = $schema_attr->getType();
$syntax = $schema_attr->getSyntaxOID();
}
if (strcasecmp($type,'Certificate') == 0 ||
strcasecmp($type,'Binary') == 0 ||
strcasecmp($attr_name,'usercertificate') == 0 ||
strcasecmp($attr_name,'usersmimecertificate') == 0 ||
strcasecmp($attr_name,'networkaddress') == 0 ||
strcasecmp($attr_name,'objectGUID') == 0 ||
strcasecmp($attr_name,'objectSID') == 0 ||
strcasecmp($attr_name,'jpegPhoto') == 0 ||
$syntax == '1.3.6.1.4.1.1466.115.121.1.10' ||
$syntax == '1.3.6.1.4.1.1466.115.121.1.28' ||
$syntax == '1.3.6.1.4.1.1466.115.121.1.5' ||
$syntax == '1.3.6.1.4.1.1466.115.121.1.8' ||
$syntax == '1.3.6.1.4.1.1466.115.121.1.9'
) {
$attr_cache[$this->server_id][$attr_name] = true;
return true;
} else {
$attr_cache[$this->server_id][$attr_name] = false;
return false;
}
}
/**
* Returns true if the specified attribute is configured as read only
* in config.php with the $read_only_attrs array.
* Attributes are configured as read-only in config.php thus:
*
* $read_only_attrs = array( "objectClass", "givenName" );
*
*
* @param string $attr The name of the attribute to test.
* @return bool
*/
function isAttrReadOnly($attr) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s)',17,__FILE__,__LINE__,__METHOD__,$attr);
$read_only_attrs = isset($_SESSION[APPCONFIG]->read_only_attrs) ? $_SESSION[APPCONFIG]->read_only_attrs : array();
$read_only_except_dn = isset($_SESSION[APPCONFIG]->read_only_except_dn) ? $_SESSION[APPCONFIG]->read_only_except_dn : '';
$attr = trim($attr);
if (! $attr)
return false;
if (! isset($read_only_attrs))
return false;
if (! is_array($read_only_attrs))
return false;
# Is the user excluded?
if (isset($read_only_except_dn) && $read_only_except_dn && $this->userIsMember($this->getLoggedInDN(),$read_only_except_dn))
return false;
foreach ($read_only_attrs as $attr_name)
if (strcasecmp($attr,trim($attr_name)) == 0)
return true;
return false;
}
/**
* Returns true if the specified attribute is configured as hidden
* in config.php with the $hidden_attrs array or the $hidden_attrs_ro
* array.
* Attributes are configured as hidden in config.php thus:
*
* $hidden_attrs = array( "objectClass", "givenName" );
*
* or
*
* $hidden_attrs_ro = array( "objectClass", "givenName", "shadowWarning",
* "shadowLastChange", "shadowMax", "shadowFlag",
* "shadowInactive", "shadowMin", "shadowExpire" );
*
*
* @param string $attr The name of the attribute to test.
* @return bool
*/
function isAttrHidden($attr) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s)',17,__FILE__,__LINE__,__METHOD__,$attr);
$hidden_attrs = isset($_SESSION[APPCONFIG]->hidden_attrs) ? $_SESSION[APPCONFIG]->hidden_attrs : array();
$hidden_attrs_ro = isset($_SESSION[APPCONFIG]->hidden_attrs_ro) ? $_SESSION[APPCONFIG]->hidden_attrs_ro : array();
$hidden_except_dn = isset($_SESSION[APPCONFIG]->hidden_except_dn) ? $_SESSION[APPCONFIG]->hidden_except_dn : '';
$attr = trim($attr);
if (! $attr)
return false;
if (! isset($hidden_attrs))
return false;
if (! is_array($hidden_attrs))
return false;
if (! isset($hidden_attrs_ro))
$hidden_attrs_ro = $hidden_attrs;
if (! is_array($hidden_attrs_ro))
$hidden_attrs_ro = $hidden_attrs;
# Is the user excluded?
if (isset($hidden_except_dn) && $hidden_except_dn && $this->userIsMember($this->getLoggedInDN(),$hidden_except_dn))
return false;
if ($this->isReadOnly()) {
foreach ($hidden_attrs_ro as $attr_name)
if (strcasecmp($attr,trim($attr_name)) == 0)
return true;
} else {
foreach ($hidden_attrs as $attr_name)
if (strcasecmp($attr,trim($attr_name)) == 0)
return true;
}
return false;
}
/**
* Fetches the password of the currently logged in user (for auth_types "cookie", "session" and "http" only)
* or false if the current login is anonymous.
*
* @return string
* @see have_auth_info
* @see getLoggedInDN
*/
function getLoggedInPass() {
if (DEBUG_ENABLED)
debug_log('Entered with ()',17,__FILE__,__LINE__,__METHOD__);
if (! $this->auth_type)
return false;
switch ($this->auth_type) {
case 'cookie':
$cookie_name = sprintf('pla_login_pass_%s',$this->server_id);
$pass = isset($_COOKIE[$cookie_name]) ? $_COOKIE[$cookie_name] : false;
if ($pass == '0')
return null;
else
return pla_blowfish_decrypt($pass);
break;
case 'session':
$session_var_name = sprintf('pla_login_pass_%s',$this->server_id);
$pass = isset($_SESSION[$session_var_name]) ? $_SESSION[$session_var_name] : false;
if ($pass == '0')
return null;
else
return pla_blowfish_decrypt($pass);
break;
case 'http':
$pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : false;
if ($pass == '0')
return null;
else
return $pass;
break;
case 'config':
return $this->login_pass;
break;
default:
error(sprintf(_('Unknown auth_type: %s'),htmlspecialchars($this->auth_type)),'error','index.php');
}
}
/**
* Returns the DN who is logged in currently to the given server, which may
* either be a DN or the string 'anonymous'. This applies only for auth_types
* "cookie", "session" and "http".
*
* One place where this function is used is the tree viewer:
* After a user logs in, the text "Logged in as: " is displayed under the server
* name. This information is retrieved from this function.
*
* @return string
* @see have_auth_info
* @see getLoggedInPass
*/
function getLoggedInDN() {
# Set default return
$return = false;
if (DEBUG_ENABLED)
debug_log('auth_type is [%s]',66,__FILE__,__LINE__,__METHOD__,$this->auth_type);
if ($this->auth_type) {
switch ($this->auth_type) {
case 'cookie':
$cookie_name = sprintf('pla_login_dn_%s',$this->server_id);
if (isset($_COOKIE[$cookie_name]))
$return = pla_blowfish_decrypt($_COOKIE[$cookie_name]);
else
$return = false;
break;
case 'session':
$session_var_name = sprintf('pla_login_dn_%s',$this->server_id);
if (isset($_SESSION[$session_var_name]))
$return = pla_blowfish_decrypt($_SESSION[$session_var_name]);
else
$return = false;
break;
case 'http':
if (isset($_SERVER['PHP_AUTH_USER'])) {
if ($this->isLoginAttrEnabled()) {
if ($this->isLoginStringEnabled()) {
$return = str_replace('
* Array (
* [creatorsName] => Array (
* [0] => "cn=Admin,dc=example,dc=com"
* )
* [createTimeStamp]=> Array (
* [0] => "10401040130"
* )
* [hasSubordinates] => Array (
* [0] => "FALSE"
* )
* )
*
*
* @param string $dn The DN of the entry whose interal attributes are desired.
* @param int $deref For aliases and referrals, this parameter specifies whether to
* follow references to the referenced DN or to fetch the attributes for
* the referencing DN. See http://php.net/ldap_search for the 4 valid
* options.
* @return array An associative array whose keys are attribute names and whose values
* are arrays of values for the aforementioned attribute.
*/
function getDNSysAttrs($dn,$deref=LDAP_DEREF_NEVER) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s)',17,__FILE__,__LINE__,__METHOD__,$dn,$deref);
$attrs = array('creatorsname','createtimestamp','modifiersname',
'structuralObjectClass','entryUUID','modifytimestamp',
'subschemaSubentry','hasSubordinates','+');
$search = $this->search(null,$dn,'(objectClass=*)',$attrs,'base',false,$deref);
$return_attrs = array();
foreach ($search as $dn => $attrs)
foreach ($attrs as $attr => $value) {
if (is_array($value)) {
foreach ($value as $val) $return_attrs[$attr][] = $val;
} else {
$return_attrs[$attr][] = $value;
}
}
return $return_attrs;
}
/**
* Gets the user defined operational attributes for an entry. Given a DN, this function fetches that entry's
* operational (ie, system or internal) attributes.
*
* These attributes should be treated as internal attributes.
*
* The returned associative array is of this form:
*
* Array (
* [nsroleDN] => Array (
* [0] => "cn=nsManagedDisabledRole,dc=example,dc=com",
* [1] => "cn=nsDisabledRole,dc=example,dc=com",
* [2] => "cn=nsAdminRole,dc=example,dc=com"
* )
* [passwordExpirationTime]=> Array (
* [0] => "20080314183611Z"
* )
* [passwordAllowChangeTime] => Array (
* [0] => "20080116175354Z"
* )
* )
*
*
* @param string $dn The DN of the entry whose interal attributes are desired.
* @param int $deref For aliases and referrals, this parameter specifies whether to
* follow references to the referenced DN or to fetch the attributes for
* the referencing DN. See http://php.net/ldap_search for the 4 valid
* options.
* @return array An associative array whose keys are attribute names and whose values
* are arrays of values for the aforementioned attribute.
*/
function getCustomDNSysAttrs($dn,$deref=LDAP_DEREF_NEVER) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s)',17,__FILE__,__LINE__,__METHOD__,$dn,$deref);
$attrs = $this->custom_sys_attrs;
$search = $this->search(null,$dn,'(objectClass=*)',$attrs,'base',false,$deref);
$return_attrs = array();
foreach ($search as $dn => $attrs)
foreach ($attrs as $attr => $value) {
if (is_array($value)) {
foreach ($value as $val) $return_attrs[$attr][] = $val;
} else {
$return_attrs[$attr][] = $value;
}
}
return $return_attrs;
}
/**
* Gets the user defined operational attributes for an entry. Given a DN, this function fetches that entry's
* operational (ie, system or internal) attributes.
*
* These attributes should be treated as regular attributes, not as internal attributes.
*
* The returned associative array is of this form:
*
* Array (
* [nsroleDN] => Array (
* [0] => "cn=nsManagedDisabledRole,dc=example,dc=com",
* [1] => "cn=nsDisabledRole,dc=example,dc=com",
* [2] => "cn=nsAdminRole,dc=example,dc=com"
* )
* [passwordExpirationTime]=> Array (
* [0] => "20080314183611Z"
* )
* [passwordAllowChangeTime] => Array (
* [0] => "20080116175354Z"
* )
* )
*
*
* @param string $dn The DN of the entry whose interal attributes are desired.
* @param int $deref For aliases and referrals, this parameter specifies whether to
* follow references to the referenced DN or to fetch the attributes for
* the referencing DN. See http://php.net/ldap_search for the 4 valid
* options.
* @return array An associative array whose keys are attribute names and whose values
* are arrays of values for the aforementioned attribute.
*/
function getCustomDNAttrs($dn,$deref=LDAP_DEREF_NEVER) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s)',17,__FILE__,__LINE__,__METHOD__,$dn,$deref);
$attrs = $this->custom_attrs;
$search = $this->search(null,$dn,'(objectClass=*)',$attrs,'base',false,$deref);
$return_attrs = array();
foreach ($search as $dn => $attrs)
foreach ($attrs as $attr => $value) {
if (is_array($value)) {
foreach ($value as $val) $return_attrs[$attr][] = $val;
} else {
$return_attrs[$attr][] = $value;
}
}
return $return_attrs;
}
/**
* Gets the attributes/values of an entry. Returns an associative array whose
* keys are attribute value names and whose values are arrays of values for
* said attribute. Optionally, callers may specify true for the parameter
* $lower_case_attr_names to force all keys in the associate array (attribute
* names) to be lower case.
*
* Sample return value of getDNAttrs( 0, "cn=Bob,ou=pepole,dc=example,dc=com" )
*
*
* Array (
* [objectClass] => Array (
* [0] => person
* [1] => top
* )
* [cn] => Array (
* [0] => Bob
* )
* [sn] => Array (
* [0] => Jones
* )
* [dn] => Array (
* [0] => cn=Bob,ou=pepole,dc=example,dc=com
* )
* )
*
*
* @param string $dn The distinguished name (DN) of the entry whose attributes/values to fetch.
* @param bool $lower_case_attr_names (optional) If true, all keys of the returned associative
* array will be lower case. Otherwise, they will be cased as the LDAP server returns
* them.
* @param int $deref For aliases and referrals, this parameter specifies whether to
* follow references to the referenced DN or to fetch the attributes for
* the referencing DN. See http://php.net/ldap_search for the 4 valid
* options.
* @return array
* @see getDNSysAttrs
* @see getDNAttr
*/
function getDNAttrs($dn,$lower_case_attr_names=false,$deref=LDAP_DEREF_NEVER) {
global $CACHE;
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s,%s)',17,__FILE__,__LINE__,__METHOD__,$dn,$lower_case_attr_names,$deref);
$attrs = null;
if (isset($CACHE['dnattrs'][$dn])) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s,%s), Returning CACHED (%s)',17,__FILE__,__LINE__,__METHOD__,
$dn,$lower_case_attr_names,$deref,$CACHE['dnattrs'][$dn]);
$attrs = $CACHE['dnattrs'][$dn];
} else {
$attrs = $this->search(null,dn_escape($dn),'(objectClass=*)',array(),'base',false,$deref);
if (count($attrs))
$attrs = array_pop($attrs);
foreach ($attrs as $key => $values)
if (! is_array($attrs[$key]))
$attrs[$key] = array($attrs[$key]);
$CACHE['dnattrs'][$dn] = $attrs;
}
if ($lower_case_attr_names)
$attrs = array_change_key_case($attrs);
return $attrs;
}
/**
* Much like getDNAttrs(), but only returns the values for
* one attribute of an object. Example calls:
*
*
* print_r( getDNAttr( 0, "cn=Bob,ou=people,dc=example,dc=com", "sn" ) );
* Array (
* [0] => "Smith"
* )
*
* print_r( getDNAttr( 0, "cn=Bob,ou=people,dc=example,dc=com", "objectClass" ) );
* Array (
* [0] => "top"
* [1] => "person"
* )
*
*
* @param string $dn The distinguished name (DN) of the entry whose attributes/values to fetch.
* @param string $attr The attribute whose value(s) to return (ie, "objectClass", "cn", "userPassword")
* @param bool $lower_case_attr_names (optional) If true, all keys of the returned associative
* array will be lower case. Otherwise, they will be cased as the LDAP server returns
* them.
* @param int $deref For aliases and referrals, this parameter specifies whether to
* follow references to the referenced DN or to fetch the attributes for
* the referencing DN. See http://php.net/ldap_search for the 4 valid
* options.
* @see getDNAttrs
* @return array
*/
function getDNAttr($dn,$attr,$lower_case_attr_names=false,$deref=LDAP_DEREF_NEVER) {
if (DEBUG_ENABLED)
debug_log('Entered with (%s,%s,%s,%s)',17,__FILE__,__LINE__,__METHOD__,$dn,$attr,$lower_case_attr_names,$deref);
if ($lower_case_attr_names)
$attr = strtolower($attr);
$attrs = $this->getDNAttrs($dn,$lower_case_attr_names,$deref);
if (isset($attrs[$attr]))
return $attrs[$attr];
else
return array();
}
/**
* Given a DN string, this returns the top container portion of the string.
* @param string $dn The DN whose container string to return.
* @return string The container
* @see get_rdn
* @see get_container
*/
function getContainerTop($dn) {
$return = $dn;
foreach ($this->getBaseDN() as $base_dn) {
if (preg_match("/${base_dn}$/",$dn)) {
$return = $base_dn;
break;
}
}
if (DEBUG_ENABLED)
debug_log('Entered with (%s), Returning (%s)',17,__FILE__,__LINE__,__METHOD__,$dn,$return);
return $return;
}
/**
* Given a DN string and a path like syntax, this returns the parent container portion of the string.
* @param string $dn The DN whose container string to return.
* @param string $path Either '/', '.' or something like '../../