1066 lines
37 KiB
PHP
1066 lines
37 KiB
PHP
<?php
|
||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||
// +--------------------------------------------------------------------------+
|
||
// | Net_LDAP |
|
||
// +--------------------------------------------------------------------------+
|
||
// | Copyright (c) 1997-2003 The PHP Group |
|
||
// +--------------------------------------------------------------------------+
|
||
// | 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 (at your option) 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 |
|
||
// +--------------------------------------------------------------------------+
|
||
// | Authors: Tarjej Huse |
|
||
// | Jan Wagner |
|
||
// +--------------------------------------------------------------------------+
|
||
//
|
||
// $Id: LDAP.php 4846 2006-02-06 11:06:47Z nbm $
|
||
|
||
require_once('PEAR.php');
|
||
require_once('LDAP/Entry.php');
|
||
require_once('LDAP/Search.php');
|
||
|
||
|
||
/**
|
||
* Error constants for errors that are not LDAP errors
|
||
*/
|
||
|
||
define ('NET_LDAP_ERROR', 1000);
|
||
|
||
|
||
/**
|
||
* Net_LDAP - manipulate LDAP servers the right way!
|
||
*
|
||
* @author Tarjei Huse
|
||
* @author Jan Wagner
|
||
* @version $Revision: 4846 $
|
||
* @package Net_LDAP
|
||
*/
|
||
class Net_LDAP extends PEAR
|
||
{
|
||
/**
|
||
* Class configuration array
|
||
*
|
||
* dn = the DN to bind as.
|
||
* host = the ldap host to connect to
|
||
* password = no explanation needed
|
||
* base = ldap base
|
||
* port = the server port
|
||
* tls = when set, ldap_start_tls() is run after connecting.
|
||
* version = ldap version (defaults to v 3)
|
||
* filter = default search filter
|
||
* scope = default search scope
|
||
*
|
||
* @access private
|
||
* @var array
|
||
*/
|
||
var $_config = array('dn' => '',
|
||
'host' => 'localhost',
|
||
'password' => '',
|
||
'tls' => false,
|
||
'base' => '',
|
||
'port' => 389,
|
||
'version' => 3,
|
||
'options' => array(),
|
||
'filter' => '(objectClass=*)',
|
||
'scope' => 'sub');
|
||
|
||
/**
|
||
* LDAP resource link.
|
||
*
|
||
* @access private
|
||
* @var resource
|
||
*/
|
||
var $_link;
|
||
|
||
/**
|
||
* Net_LDAP Release Version
|
||
*
|
||
* @access private
|
||
* @var string
|
||
*/
|
||
var $_version = "0.6.6";
|
||
|
||
/**
|
||
* Net_LDAP_Schema object
|
||
*
|
||
* @access private
|
||
* @var object Net_LDAP_Schema
|
||
*/
|
||
var $_schema = null;
|
||
|
||
/**
|
||
* Cache for attribute encoding checks
|
||
*
|
||
* @access private
|
||
* @var array Hash with attribute names as key and boolean value
|
||
* to determine whether they should be utf8 encoded or not.
|
||
*/
|
||
var $_schemaAttrs = array();
|
||
|
||
/**
|
||
* Net_LDAP constructor
|
||
*
|
||
* Sets the config array
|
||
*
|
||
* @access protected
|
||
* @param array Configuration array
|
||
* @return void
|
||
* @see $_config
|
||
*/
|
||
function Net_LDAP($_config = array())
|
||
{
|
||
$this->PEAR('Net_LDAP_Error');
|
||
|
||
foreach ($_config as $k => $v) {
|
||
$this->_config[$k] = $v;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Creates the initial ldap-object
|
||
*
|
||
* Static function that returns either an error object or the new Net_LDAP object.
|
||
* Something like a factory. Takes a config array with the needed parameters.
|
||
*
|
||
* @access public
|
||
* @param array Configuration array
|
||
* @return mixed object Net_LDAP_Error or Net_LDAP
|
||
* @see $_config
|
||
*/
|
||
function &connect($config = array())
|
||
{
|
||
if (!function_exists('ldap_connect')){
|
||
return Net_LDAP::raiseError("It seems that you do not have the ldap-extension installed. Please install it before using this package.");
|
||
}
|
||
@$obj =& new Net_LDAP($config);
|
||
$err = $obj->bind();
|
||
|
||
if (Net_LDAP::isError($err)) {
|
||
return $err;
|
||
}
|
||
return $obj;
|
||
}
|
||
|
||
/**
|
||
* Bind to the ldap-server
|
||
*
|
||
* The function may be used if you do not create the object using Net_LDAP::connect.
|
||
*
|
||
* @access public
|
||
* @param array Configuration array
|
||
* @return mixed Net_LDAP_Error or true
|
||
* @see $_config
|
||
*/
|
||
function bind($config = array())
|
||
{
|
||
foreach ($config as $k => $v) {
|
||
$this->_config[$k] = $v;
|
||
}
|
||
|
||
if ($this->_config['host']) {
|
||
$this->_link = @ldap_connect($this->_config['host'], $this->_config['port']);
|
||
} else {
|
||
return $this->raiseError("Host not defined in config. {$this->_config['host']}");
|
||
}
|
||
|
||
if (!$this->_link) {
|
||
// there is no good errorcode for this one! I chose 52.
|
||
return $this->raiseError("Could not connect to server. ldap_connect failed.", 52);
|
||
}
|
||
// You must set the version and start tls BEFORE binding!
|
||
|
||
if ($this->_config['version'] != 2 && Net_LDAP::isError($msg = $this->setLDAPVersion())) {
|
||
return $msg;
|
||
}
|
||
|
||
if ($this->_config['tls'] && Net_LDAP::isError($msg = $this->startTLS())) {
|
||
return $msg;
|
||
}
|
||
|
||
if (isset($this->_config['options']) &&
|
||
is_array($this->_config['options']) &&
|
||
count($this->_config['options']))
|
||
{
|
||
foreach ($this->_config['options'] as $opt => $val) {
|
||
$err = $this->setOption($opt, $val);
|
||
if (Net_LDAP::isError($err)) {
|
||
return $err;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (isset($this->_config['dn']) && isset($this->_config['password'])) {
|
||
$bind = @ldap_bind($this->_link, $this->_config['dn'], $this->_config['password']);
|
||
} else {
|
||
$bind = @ldap_bind($this->_link);
|
||
}
|
||
|
||
if (!$bind) {
|
||
return $this->raiseError("Bind failed " . @ldap_error($this->_link), @ldap_errno($this->_link));
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* ReBind to the ldap-server using another dn and password
|
||
*
|
||
* The function may be used if you do not create the object using Net_LDAP::connect.
|
||
*
|
||
* @access public
|
||
* @param string $dn - the DN to bind as.
|
||
* string $password - the bassword to use.
|
||
* @return mixed Net_LDAP_Error or true
|
||
* @see $_config
|
||
*/
|
||
|
||
function reBind ($dn = null, $password = null)
|
||
{
|
||
|
||
if ($dn && $password ) {
|
||
$bind = @ldap_bind($this->_link, $dn, $password);
|
||
} else {
|
||
$bind = @ldap_bind($this->_link);
|
||
}
|
||
|
||
if (!$bind) {
|
||
return $this->raiseError("Bind failed " . @ldap_error($this->_link), @ldap_errno($this->_link));
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Starts an encrypted session
|
||
*
|
||
* @access public
|
||
* @return mixed True or Net_LDAP_Error
|
||
*/
|
||
function startTLS()
|
||
{
|
||
if (!@ldap_start_tls($this->_link)) {
|
||
return $this->raiseError("TLS not started. Error:" . @ldap_error($this->_link), @ldap_errno($this->_link));
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* alias function of startTLS() for perl-ldap interface
|
||
*
|
||
* @see startTLS()
|
||
*/
|
||
function start_tls()
|
||
{
|
||
$args = func_get_args();
|
||
return call_user_func_array(array($this, 'startTLS' ), $args);
|
||
}
|
||
|
||
/**
|
||
* Close LDAP connection.
|
||
*
|
||
* Closes the connection. Use this when the session is over.
|
||
*
|
||
* @return void
|
||
*/
|
||
function done()
|
||
{
|
||
$this->_Net_LDAP();
|
||
}
|
||
|
||
/**
|
||
* Destructor
|
||
*
|
||
* @access private
|
||
*/
|
||
function _Net_LDAP()
|
||
{
|
||
@ldap_close($this->_link);
|
||
}
|
||
|
||
/**
|
||
* Add a new entryobject to a directory.
|
||
*
|
||
* Use add to add a new Net_LDAP_Entry object to the directory.
|
||
*
|
||
* @param object Net_LDAP_Entry
|
||
* @return mixed Net_LDAP_Error or true
|
||
*/
|
||
function add($entry)
|
||
{
|
||
if (@ldap_add($this->_link, $entry->dn(), $entry->attributes())) {
|
||
return true;
|
||
} else {
|
||
return $this->raiseError("Could not add entry " . $entry->dn() . " " . @ldap_error($this->_link),
|
||
@ldap_errno($this->_link));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Delete an entry from the directory
|
||
*
|
||
* The object may either be a string representing the dn or a Net_LDAP_Entry object.
|
||
* The param array may contain a boolean value named recursive. When set, all subentries
|
||
* of the Entry will be deleted as well
|
||
*
|
||
* @access public
|
||
* @param mixed string or Net_LDAP_Entry
|
||
* @param array
|
||
* @return mixed Net_LDAP_Error or true
|
||
*/
|
||
function delete($dn, $param = array())
|
||
{
|
||
if (is_object($dn) && strtolower(get_class($dn)) == 'net_ldap_entry') {
|
||
$dn = $dn->dn();
|
||
} else {
|
||
if (!is_string($dn)) {
|
||
// this is what the server would say: invalid_dn_syntax.
|
||
return $this->raiseError("$dn not a string nor an entryobject!", 34);
|
||
}
|
||
}
|
||
|
||
if ($param['recursive'] ) {
|
||
$searchresult = @ldap_list($this->_link, $dn, '(objectClass=*)', array());
|
||
|
||
if ($searchresult) {
|
||
$entries = @ldap_get_entries($this->_link, $searchresult);
|
||
|
||
for ($i=0; $i<$entries['count']; $i++) {
|
||
$result = $this->delete($entries[$i]['dn'], array('recursive' => true));
|
||
if (!$result) {
|
||
$errno = @ldap_errno($this->_link);
|
||
return $this->raiseMessage ("Net_LDAP::delete: " . $this->errorMessage($errno), $errno);
|
||
}
|
||
if(PEAR::isError($result)){
|
||
return $result;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (!@ldap_delete($this->_link, $dn)) {
|
||
$error = ldap_errno($this->_link );
|
||
if ($error == 66) {
|
||
/* entry has subentries */
|
||
return $this->raiseError('Net_LDAP::delete: Cound not delete entry ' . $dn .
|
||
' because of subentries. Use the recursive param to delete them.');
|
||
} else {
|
||
return $this->raiseError("Net_LDAP::delete: Could not delete entry " . $dn ." because: ".
|
||
$this->errorMessage($error), $error);
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Modify an ldapentry
|
||
*
|
||
* This is taken from the perlpod of net::ldap, and explains things quite nicely.
|
||
* modify ( DN, OPTIONS )
|
||
* Modify the contents of DN on the server. DN May be a
|
||
* string or a Net::LDAP::Entry object.
|
||
*
|
||
* dn This option is here for compatibility only, and
|
||
* may be removed in future. Previous releases did
|
||
* not take the DN argument which replaces this
|
||
* option.
|
||
*
|
||
* add The add option should be a reference to a HASH.
|
||
* The values of the HASH are the attributes to add,
|
||
* and the values may be a string or a reference to a
|
||
* list of values.
|
||
*
|
||
* delete
|
||
* A reference to an ARRAY of attributes to delete.
|
||
* TODO: This does not support deleting one or two values yet - use
|
||
* replace.
|
||
*
|
||
* replace
|
||
* The <replace> option takes a argument in the same
|
||
* form as add, but will cause any existing
|
||
* attributes with the same name to be replaced. If
|
||
* the value for any attribute in the <20>rray is a ref<65>
|
||
* erence to an empty string the all instances of the
|
||
* attribute will be deleted.
|
||
*
|
||
* changes
|
||
* This is an alternative to add, delete and replace
|
||
* where the whole operation can be given in a single
|
||
* argument. The argument should be a array
|
||
*
|
||
* Values in the ARRAY are used in pairs, the first
|
||
* is the operation add, delete or replace and the
|
||
* second is a reference to an ARRAY of attribute
|
||
* values.
|
||
*
|
||
* The attribute value list is also used in pairs.
|
||
* The first value in each pair is the attribute name
|
||
* and the second is a reference to a list of values.
|
||
*
|
||
* Example:
|
||
* $ldap->modify ( $dn, array (changes => array(
|
||
* 'delete' => array('faxNumber' => ''),
|
||
* 'add' => array('sn' => 'Barr'),
|
||
* 'replace' => array(email => 'tarjei@nu.no'))));
|
||
*
|
||
* @access public
|
||
* @param string
|
||
* @param array
|
||
* @return mixed Net_LDAP_Error or true
|
||
*/
|
||
function modify($dn , $params = array())
|
||
{
|
||
if (is_object($dn)) {
|
||
$dn = $dn->dn();
|
||
}
|
||
// since $params['dn'] is not used in net::ldap now:
|
||
if (isset($params['dn'])) {
|
||
return $this->raiseError("This feature will not be implemented!");
|
||
}
|
||
// new code from rafael at krysciak dot de
|
||
if(array_key_exists('changes', $params)) {
|
||
$_params = $params;
|
||
} else {
|
||
$_params['changes'] = $params;
|
||
}
|
||
if (is_array($_params['changes'])) {
|
||
foreach($_params['changes'] AS $option => $atrr) {
|
||
switch($option) {
|
||
case 'add':
|
||
$command = $dn_exists ? 'ldap_mod_add':'ldap_add';
|
||
break;
|
||
case 'replace':
|
||
$command = 'ldap_mod_replace';
|
||
break;
|
||
case 'delete':
|
||
$command = 'ldap_mod_del';
|
||
// to delete an attribute with a specific value you
|
||
// need a hash array('attr_name' => array('attr_value_1', ... ,'attr_value_n'))
|
||
// the hash array('attr_name' => 'attr_value') will be converted
|
||
// automatically to array('attr_name' => array('attr_value'))
|
||
foreach($atrr AS $atrr_field => $atrr_value) {
|
||
if(!is_array($atrr_value)) {
|
||
$atrr[$atrr_field] = array($atrr_value);
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
return $this->raiseError("Net_LDAP::modify: not supported option " . $option);
|
||
break;
|
||
} // end switch($option) {
|
||
|
||
if(!@call_user_func($command, $this->_link, $dn, $atrr)) {
|
||
return $this->raiseError("Net_LDAP::modify: $dn not modified because:" . ldap_error($this->_link), ldap_errno($this->_link));
|
||
}
|
||
} // end foreach($_params['changes'] AS $option => $atrr) {
|
||
} // end if (is_array($_params['changes'])) {
|
||
// everything went fine :)
|
||
return true;
|
||
|
||
/* old broken code see bug#2987
|
||
if (isset($params['changes'])) {
|
||
|
||
if (isset($params['changes']['add']) &&
|
||
!@ldap_modify($this->_link, $dn, $params['changes']['add'])) {
|
||
|
||
return $this->raiseError("Net_LDAP::modify: $dn not modified because:" . ldap_error($this->_link),
|
||
ldap_errno($this->_link));
|
||
}
|
||
|
||
if (isset($params['changes']['replace']) &&
|
||
!@ldap_modify($this->_link, $dn, $params['changes']['replace'])) {
|
||
|
||
return $this->raiseError("Net_LDAP::modify: replace change didn't work: " . ldap_error($this->_link),
|
||
ldap_errno($this->_link));
|
||
}
|
||
|
||
if (isset($params['changes']['delete']) &&
|
||
!@ldap_mod_del($this->_link, $dn, $params['changes']['delete'])) {
|
||
|
||
return $this->raiseError("Net_LDAP::modify:delete did not work" . ldap_error($this->_link),
|
||
ldap_errno($this->_link));
|
||
}
|
||
}
|
||
|
||
if (isset($params['add']) && !@ldap_add($this->_link, $dn, $params['add'])) {
|
||
return $this->raiseError(ldap_error($this->_link), ldap_errno($this->_link));
|
||
}
|
||
|
||
if (isset($params['replace']) && !@ldap_modify($this->_link, $dn, $params['replace'])) {
|
||
return $this->raiseError(ldap_error($this->_link), ldap_errno($this->_link));
|
||
}
|
||
|
||
if (isset($params['delete'])) {
|
||
// since you delete an attribute by making it empty:
|
||
foreach ($params['delete'] as $k) {
|
||
$params['delete'][$k] = '';
|
||
}
|
||
|
||
if (!@ldap_modify($this->_link, $dn, $params['delete'])) {
|
||
return $this->raiseError(ldap_error($this->_link), ldap_errno($this->_link));
|
||
}
|
||
}
|
||
// everything went fine :)
|
||
return true;
|
||
*/
|
||
|
||
}
|
||
|
||
/**
|
||
* Run a ldap query
|
||
*
|
||
* Search is used to query the ldap-database.
|
||
* $base and $filter may be ommitted. BaseDN and default filter will then be used.
|
||
* Params may contain:
|
||
*
|
||
* scope: The scope which will be used for searching
|
||
* base - Just one entry
|
||
* sub - The whole tree
|
||
* one - Immediately below $base
|
||
* sizelimit: Limit the number of entries returned (default: 0),
|
||
* timelimit: Limit the time spent for searching (default: 0),
|
||
* attrsonly: If true, the search will only return the attribute names, NO values
|
||
* attributes: Array of attribute names, which the entry should contain. It is good practice
|
||
* to limit this to just the ones you need, so by default this function does not
|
||
* return any attributes at all.
|
||
* [NOT IMPLEMENTED]
|
||
* deref: By default aliases are dereferenced to locate the base object for the search, but not when
|
||
* searching subordinates of the base object. This may be changed by specifying one of the
|
||
* following values:
|
||
*
|
||
* never - Do not dereference aliases in searching or in locating the base object of the search.
|
||
* search - Dereference aliases in subordinates of the base object in searching, but not in
|
||
* locating the base object of the search.
|
||
* find
|
||
* always
|
||
*
|
||
* @access public
|
||
* @param string LDAP searchbase
|
||
* @param string LDAP search filter
|
||
* @param array Array of options
|
||
* @return object mixed Net_LDAP_Search or Net_LDAP_Error
|
||
*/
|
||
function search($base = null, $filter = null, $params = array())
|
||
{
|
||
if (is_null($base)) {
|
||
$base = $this->_config['base'];
|
||
}
|
||
if (is_null($filter)) {
|
||
$filter = $this->_config['filter'];
|
||
}
|
||
|
||
/* setting searchparameters */
|
||
(isset($params['sizelimit'])) ? $sizelimit = $params['sizelimit'] : $sizelimit = 0;
|
||
(isset($params['timelimit'])) ? $timelimit = $params['timelimit'] : $timelimit = 0;
|
||
(isset($params['attrsonly'])) ? $attrsonly = $params['attrsonly'] : $attrsonly = 0;
|
||
(isset($params['attributes'])) ? $attributes = $params['attributes'] : $attributes = array('');
|
||
|
||
if (!is_array($attributes)) {
|
||
$this->raiseError("The param attributes must be an array!");
|
||
}
|
||
|
||
/* scoping makes searches faster! */
|
||
$scope = (isset($params['scope']) ? $params['scope'] : $this->_config['scope']);
|
||
|
||
switch ($scope) {
|
||
case 'one':
|
||
$search_function = 'ldap_list';
|
||
break;
|
||
case 'base':
|
||
$search_function = 'ldap_read';
|
||
break;
|
||
default:
|
||
$search_function = 'ldap_search';
|
||
}
|
||
|
||
$search = @call_user_func($search_function,
|
||
$this->_link,
|
||
$base,
|
||
$filter,
|
||
$attributes,
|
||
$attrsonly,
|
||
$sizelimit,
|
||
$timelimit);
|
||
|
||
if ($err = ldap_errno($this->_link)) {
|
||
|
||
if ($err == 32) {
|
||
// Errorcode 32 = no such object, i.e. a nullresult.
|
||
return $obj =& new Net_LDAP_Search ($search, $this->_link);
|
||
|
||
// Errorcode 4 = sizelimit exeeded. this will be handled better in time...
|
||
//} elseif ($err == 4) {
|
||
// return $obj = & new Net_LDAP_Search ($search, $this->_link);
|
||
|
||
} elseif ($err == 87) {
|
||
// bad search filter
|
||
return $this->raiseError($this->errorMessage($err) . "($filter)", $err);
|
||
} else {
|
||
$msg = "\nParameters:\nBase: $base\nFilter: $filter\nScope: $scope";
|
||
return $this->raiseError($this->errorMessage($err) . $msg, $err);
|
||
}
|
||
} else {
|
||
@$obj =& new Net_LDAP_Search($search, $this->_link);
|
||
return $obj;
|
||
}
|
||
|
||
}
|
||
|
||
/**
|
||
* Set an LDAP option
|
||
*
|
||
* @access public
|
||
* @param string Option to set
|
||
* @param mixed Value to set Option to
|
||
* @return mixed Net_LDAP_Error or true
|
||
*/
|
||
function setOption($option, $value)
|
||
{
|
||
if ($this->_link) {
|
||
if (defined($option)) {
|
||
if (@ldap_set_option($this->_link, constant($option), $value)) {
|
||
return true;
|
||
} else {
|
||
$err = @ldap_errno($this->_link);
|
||
if ($err) {
|
||
$msg = @ldap_err2str($err);
|
||
} else {
|
||
$err = NET_LDAP_ERROR;
|
||
$msg = $this->errorMessage($err);
|
||
}
|
||
return $this->raiseError($msg, $err);
|
||
}
|
||
} else {
|
||
return $this->raiseError("Unkown Option requested");
|
||
}
|
||
} else {
|
||
return $this->raiseError("No LDAP connection");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get an LDAP option value
|
||
*
|
||
* @access public
|
||
* @param string Option to get
|
||
* @return mixed Net_LDAP_Error or option value
|
||
*/
|
||
function getOption($option)
|
||
{
|
||
if ($this->_link) {
|
||
if (defined($option)) {
|
||
if (@ldap_get_option($this->_link, constant($option), $value)) {
|
||
return $value;
|
||
} else {
|
||
$err = @ldap_errno($this->_link);
|
||
if ($err) {
|
||
$msg = @ldap_err2str($err);
|
||
} else {
|
||
$err = NET_LDAP_ERROR;
|
||
$msg = $this->errorMessage($err);
|
||
}
|
||
return $this->raiseError($msg, $err);
|
||
}
|
||
} else {
|
||
$this->raiseError("Unkown Option requested");
|
||
}
|
||
} else {
|
||
$this->raiseError("No LDAP connection");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get the LDAP_PROTOCOL_VERSION that is used on the connection.
|
||
*
|
||
* A lot of ldap functionality is defined by what protocol version the ldap server speaks.
|
||
* This might be 2 or 3.
|
||
*
|
||
* @return int
|
||
*/
|
||
function getLDAPVersion()
|
||
{
|
||
if($this->_link) {
|
||
$version = $this->getOption("LDAP_OPT_PROTOCOL_VERSION");
|
||
} else {
|
||
$version = $this->_config['version'];
|
||
}
|
||
return $version;
|
||
}
|
||
|
||
/**
|
||
* Set the LDAP_PROTOCOL_VERSION that is used on the connection.
|
||
*
|
||
* @param int Version to set
|
||
* @return mixed Net_LDAP_Error or TRUE
|
||
*/
|
||
function setLDAPVersion($version = 0)
|
||
{
|
||
if (!$version) {
|
||
$version = $this->_config['version'];
|
||
}
|
||
return $this->setOption("LDAP_OPT_PROTOCOL_VERSION", $version);
|
||
}
|
||
|
||
/**
|
||
* Get the Net_LDAP version.
|
||
*
|
||
* Return the Net_LDAP version
|
||
*
|
||
* @return string Net_LDAP version
|
||
*/
|
||
function getVersion ()
|
||
{
|
||
return $this->_version;
|
||
}
|
||
|
||
/**
|
||
* Tell if a dn already exists
|
||
*
|
||
* @param string
|
||
* @return boolean
|
||
*/
|
||
function dnExists($dn)
|
||
{
|
||
$dns = explode(",",$dn);
|
||
$filter = array_shift($dns);
|
||
$base= implode($dns,',');
|
||
//$base = $dn;
|
||
//$filter = '(objectclass=*)';
|
||
|
||
$result = @ldap_list($this->_link, $base, $filter, array(), 1, 1);
|
||
if (ldap_errno($this->_link) == 32) {
|
||
return false;
|
||
}
|
||
if (ldap_errno($this->_link) != 0) {
|
||
$this->raiseError(ldap_error($this->_link), ldap_errno($this->_link));
|
||
}
|
||
if (@ldap_count_entries($this->_link, $result)) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
/**
|
||
* Get a specific entry based on the dn
|
||
*
|
||
* @param string dn
|
||
* @param array Array of Attributes to select
|
||
* @return object Net_LDAP_Entry or Net_LDAP_Error
|
||
*/
|
||
function &getEntry($dn, $attr = array(''))
|
||
{
|
||
$result = $this->search($dn, '(objectClass=*)', array('scope' => 'base', 'attributes' => $attr));
|
||
if (Net_LDAP::isError($result)) {
|
||
return $result;
|
||
}
|
||
$entry = $result->shiftEntry();
|
||
if (false == $entry) {
|
||
return $this->raiseError('Could not fetch entry');
|
||
}
|
||
return $entry;
|
||
}
|
||
|
||
|
||
/**
|
||
* Returns the string for an ldap errorcode.
|
||
*
|
||
* Made to be able to make better errorhandling
|
||
* Function based on DB::errorMessage()
|
||
* Tip: The best description of the errorcodes is found here: http://www.directory-info.com/LDAP/LDAPErrorCodes.html
|
||
*
|
||
* @param int Error code
|
||
* @return string The errorstring for the error.
|
||
*/
|
||
function errorMessage($errorcode)
|
||
{
|
||
$errorMessages = array(
|
||
0x00 => "LDAP_SUCCESS",
|
||
0x01 => "LDAP_OPERATIONS_ERROR",
|
||
0x02 => "LDAP_PROTOCOL_ERROR",
|
||
0x03 => "LDAP_TIMELIMIT_EXCEEDED",
|
||
0x04 => "LDAP_SIZELIMIT_EXCEEDED",
|
||
0x05 => "LDAP_COMPARE_FALSE",
|
||
0x06 => "LDAP_COMPARE_TRUE",
|
||
0x07 => "LDAP_AUTH_METHOD_NOT_SUPPORTED",
|
||
0x08 => "LDAP_STRONG_AUTH_REQUIRED",
|
||
0x09 => "LDAP_PARTIAL_RESULTS",
|
||
0x0a => "LDAP_REFERRAL",
|
||
0x0b => "LDAP_ADMINLIMIT_EXCEEDED",
|
||
0x0c => "LDAP_UNAVAILABLE_CRITICAL_EXTENSION",
|
||
0x0d => "LDAP_CONFIDENTIALITY_REQUIRED",
|
||
0x0e => "LDAP_SASL_BIND_INPROGRESS",
|
||
0x10 => "LDAP_NO_SUCH_ATTRIBUTE",
|
||
0x11 => "LDAP_UNDEFINED_TYPE",
|
||
0x12 => "LDAP_INAPPROPRIATE_MATCHING",
|
||
0x13 => "LDAP_CONSTRAINT_VIOLATION",
|
||
0x14 => "LDAP_TYPE_OR_VALUE_EXISTS",
|
||
0x15 => "LDAP_INVALID_SYNTAX",
|
||
0x20 => "LDAP_NO_SUCH_OBJECT",
|
||
0x21 => "LDAP_ALIAS_PROBLEM",
|
||
0x22 => "LDAP_INVALID_DN_SYNTAX",
|
||
0x23 => "LDAP_IS_LEAF",
|
||
0x24 => "LDAP_ALIAS_DEREF_PROBLEM",
|
||
0x30 => "LDAP_INAPPROPRIATE_AUTH",
|
||
0x31 => "LDAP_INVALID_CREDENTIALS",
|
||
0x32 => "LDAP_INSUFFICIENT_ACCESS",
|
||
0x33 => "LDAP_BUSY",
|
||
0x34 => "LDAP_UNAVAILABLE",
|
||
0x35 => "LDAP_UNWILLING_TO_PERFORM",
|
||
0x36 => "LDAP_LOOP_DETECT",
|
||
0x3C => "LDAP_SORT_CONTROL_MISSING",
|
||
0x3D => "LDAP_INDEX_RANGE_ERROR",
|
||
0x40 => "LDAP_NAMING_VIOLATION",
|
||
0x41 => "LDAP_OBJECT_CLASS_VIOLATION",
|
||
0x42 => "LDAP_NOT_ALLOWED_ON_NONLEAF",
|
||
0x43 => "LDAP_NOT_ALLOWED_ON_RDN",
|
||
0x44 => "LDAP_ALREADY_EXISTS",
|
||
0x45 => "LDAP_NO_OBJECT_CLASS_MODS",
|
||
0x46 => "LDAP_RESULTS_TOO_LARGE",
|
||
0x47 => "LDAP_AFFECTS_MULTIPLE_DSAS",
|
||
0x50 => "LDAP_OTHER",
|
||
0x51 => "LDAP_SERVER_DOWN",
|
||
0x52 => "LDAP_LOCAL_ERROR",
|
||
0x53 => "LDAP_ENCODING_ERROR",
|
||
0x54 => "LDAP_DECODING_ERROR",
|
||
0x55 => "LDAP_TIMEOUT",
|
||
0x56 => "LDAP_AUTH_UNKNOWN",
|
||
0x57 => "LDAP_FILTER_ERROR",
|
||
0x58 => "LDAP_USER_CANCELLED",
|
||
0x59 => "LDAP_PARAM_ERROR",
|
||
0x5a => "LDAP_NO_MEMORY",
|
||
0x5b => "LDAP_CONNECT_ERROR",
|
||
0x5c => "LDAP_NOT_SUPPORTED",
|
||
0x5d => "LDAP_CONTROL_NOT_FOUND",
|
||
0x5e => "LDAP_NO_RESULTS_RETURNED",
|
||
0x5f => "LDAP_MORE_RESULTS_TO_RETURN",
|
||
0x60 => "LDAP_CLIENT_LOOP",
|
||
0x61 => "LDAP_REFERRAL_LIMIT_EXCEEDED",
|
||
1000 => "Unknown Net_LDAP error"
|
||
);
|
||
|
||
return isset($errorMessages[$errorcode]) ? $errorMessages[$errorcode] : $errorMessages[NET_LDAP_ERROR];
|
||
}
|
||
|
||
/**
|
||
* Tell whether value is a Net_LDAP_Error or not
|
||
*
|
||
* @access public
|
||
* @param mixed
|
||
* @return boolean
|
||
*/
|
||
function isError($value)
|
||
{
|
||
return (is_a($value, "Net_LDAP_Error") || parent::isError($value));
|
||
}
|
||
|
||
/**
|
||
* gets a root dse object
|
||
*
|
||
* @access public
|
||
* @author Jan Wagner <wagner@netsols.de>
|
||
* @param array Array of attributes to search for
|
||
* @return object mixed Net_LDAP_Error or Net_LDAP_RootDSE
|
||
*/
|
||
function &rootDse($attrs = null)
|
||
{
|
||
require_once('Net/LDAP/RootDSE.php');
|
||
|
||
if (is_array($attrs) && count($attrs) > 0 ) {
|
||
$attributes = $attrs;
|
||
} else {
|
||
$attributes = array('namingContexts',
|
||
'altServer',
|
||
'supportedExtension',
|
||
'supportedControl',
|
||
'supportedSASLMechanisms',
|
||
'supportedLDAPVersion',
|
||
'subschemaSubentry' );
|
||
}
|
||
$result = $this->search('', '(objectClass=*)', array('attributes' => $attributes, 'scope' => 'base'));
|
||
if (Net_LDAP::isError($result)) return $result;
|
||
|
||
$entry = $result->shift_entry();
|
||
if (false === $entry) return $this->raiseError('Could not fetch RootDSE entry');
|
||
|
||
return new Net_LDAP_RootDSE($entry);
|
||
}
|
||
|
||
/**
|
||
* alias function of rootDse() for perl-ldap interface
|
||
*
|
||
* @access public
|
||
* @see rootDse()
|
||
*/
|
||
function &root_dse()
|
||
{
|
||
$args = func_get_args();
|
||
return call_user_func_array(array($this, 'rootDse'), $args);
|
||
}
|
||
|
||
/**
|
||
* get a schema object
|
||
*
|
||
* @access public
|
||
* @author Jan Wagner <wagner@netsols.de>
|
||
* @param string Subschema entry dn
|
||
* @return object mixed Net_LDAP_Schema or Net_LDAP_Error
|
||
*/
|
||
function &schema($dn = null)
|
||
{
|
||
require_once('Net/LDAP/Schema.php');
|
||
|
||
$schema =& new Net_LDAP_Schema();
|
||
|
||
if (is_null($dn)) {
|
||
// get the subschema entry via root dse
|
||
$dse = $this->rootDSE(array('subschemaSubentry'));
|
||
if (false == Net_LDAP::isError($dse)) {
|
||
$base = $dse->getValue('subschemaSubentry', 'single');
|
||
if (!Net_LDAP::isError($base)) {
|
||
$dn = $base;
|
||
}
|
||
}
|
||
}
|
||
if (is_null($dn)) {
|
||
$dn = 'cn=Subschema';
|
||
}
|
||
|
||
// fetch the subschema entry
|
||
$result = $this->search($dn, '(objectClass=*)',
|
||
array('attributes' => array_values($schema->types), 'scope' => 'base'));
|
||
if (Net_LDAP::isError($result)) {
|
||
return $result;
|
||
}
|
||
|
||
$entry = $result->shift_entry();
|
||
if (false === $entry) {
|
||
return $this->raiseError('Could not fetch Subschema entry');
|
||
}
|
||
|
||
$schema->parse($entry);
|
||
|
||
return $schema;
|
||
}
|
||
|
||
/**
|
||
* Encodes given attributes to UTF8 if needed
|
||
*
|
||
* This function takes attributes in an array and then checks against the schema if they need
|
||
* UTF8 encoding. If that is so, they will be encoded. An encoded array will be returned and
|
||
* can be used for adding or modifying.
|
||
*
|
||
* @access public
|
||
* @param array Array of attributes
|
||
* @return array Array of UTF8 encoded attributes
|
||
*/
|
||
function utf8Encode($attributes)
|
||
{
|
||
return $this->_utf8($attributes, 'utf8_encode');
|
||
}
|
||
|
||
/**
|
||
* Decodes the given attribute values
|
||
*
|
||
* @access public
|
||
* @param array Array of attributes
|
||
* @return array Array with decoded attribute values
|
||
*/
|
||
function utf8Decode($attributes)
|
||
{
|
||
return $this->_utf8($attributes, 'utf8_decode');
|
||
}
|
||
|
||
/**
|
||
* Encodes or decodes attribute values if needed
|
||
*
|
||
* @access private
|
||
* @param array Array of attributes
|
||
* @param array Function to apply to attribute values
|
||
* @return array Array of attributes with function applied to values
|
||
*/
|
||
function _utf8($attributes, $function)
|
||
{
|
||
if (!$this->_schema) {
|
||
$this->_schema = $this->schema();
|
||
}
|
||
|
||
if (!$this->_link || Net_LDAP::isError($this->_schema) || !function_exists($function)) {
|
||
return $attributes;
|
||
}
|
||
|
||
if (is_array($attributes) && count($attributes) > 0) {
|
||
|
||
foreach( $attributes as $k => $v ) {
|
||
|
||
if (!isset($this->_schemaAttrs[$k])) {
|
||
|
||
$attr = $this->_schema->get('attribute', $k);
|
||
if (Net_LDAP::isError($attr)) {
|
||
continue;
|
||
}
|
||
|
||
if (false !== strpos($attr['syntax'], '1.3.6.1.4.1.1466.115.121.1.15')) {
|
||
$encode = true;
|
||
} else {
|
||
$encode = false;
|
||
}
|
||
$this->_schemaAttrs[$k] = $encode;
|
||
|
||
} else {
|
||
$encode = $this->_schemaAttrs[$k];
|
||
}
|
||
|
||
if ($encode) {
|
||
if (is_array($v)) {
|
||
foreach ($v as $ak => $av) {
|
||
$v[$ak] = call_user_func($function, $av );
|
||
}
|
||
} else {
|
||
$v = call_user_func($function, $v);
|
||
}
|
||
}
|
||
$attributes[$k] = $v;
|
||
}
|
||
}
|
||
return $attributes;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Net_LDAP_Error implements a class for reporting portable LDAP error messages.
|
||
*
|
||
* @package Net_LDAP
|
||
*/
|
||
class Net_LDAP_Error extends PEAR_Error
|
||
{
|
||
/**
|
||
* Net_LDAP_Error constructor.
|
||
*
|
||
* @param mixed Net_LDAP error code, or string with error message.
|
||
* @param integer what "error mode" to operate in
|
||
* @param integer what error level to use for $mode & PEAR_ERROR_TRIGGER
|
||
* @param mixed additional debug info, such as the last query
|
||
* @access public
|
||
* @see PEAR_Error
|
||
*/
|
||
function Net_LDAP_Error($code = NET_LDAP_ERROR, $mode = PEAR_ERROR_RETURN,
|
||
$level = E_USER_NOTICE, $debuginfo = null)
|
||
{
|
||
$mode = PEAR_ERROR_RETURN;
|
||
if (is_int($code)) {
|
||
$this->PEAR_Error('Net_LDAP_Error: ' . Net_LDAP::errorMessage($code), $code, $mode, $level, $debuginfo);
|
||
} else {
|
||
$this->PEAR_Error("Net_LDAP_Error: $code", NET_LDAP_ERROR, $mode, $level, $debuginfo);
|
||
}
|
||
}
|
||
}
|
||
?>
|