We are no longer offering accounts on this server. Consider https://gitlab.freedesktop.org/ as a place to host projects.

Commit ee41bc56 authored by mattl's avatar mattl

Updated LDAP2 extlib to latest version.

parent a6545d09
......@@ -13,7 +13,7 @@
* @author Benedikt Hallinger <beni@php.net>
* @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
* @version SVN: $Id: LDAP2.php 286788 2009-08-04 06:05:49Z beni $
* @version SVN: $Id: LDAP2.php 332308 2013-12-09 09:15:47Z beni $
* @link http://pear.php.net/package/Net_LDAP2/
*/
......@@ -39,7 +39,7 @@ define('NET_LDAP2_ERROR', 1000);
/**
* Net_LDAP2 Version
*/
define('NET_LDAP2_VERSION', '2.0.7');
define('NET_LDAP2_VERSION', '2.1.0');
/**
* Net_LDAP2 - manipulate LDAP servers the right way!
......@@ -612,30 +612,47 @@ class Net_LDAP2 extends PEAR
*/
public function startTLS()
{
// Test to see if the server supports TLS first.
// This is done via testing the extensions offered by the server.
// The OID 1.3.6.1.4.1.1466.20037 tells us, if TLS is supported.
/* Test to see if the server supports TLS first.
This is done via testing the extensions offered by the server.
The OID 1.3.6.1.4.1.1466.20037 tells us, if TLS is supported.
Note, that not all servers allow to feth either the rootDSE or
attributes over an unencrypted channel, so we must ignore errors. */
$rootDSE = $this->rootDse();
if (self::isError($rootDSE)) {
return $this->raiseError("Unable to fetch rootDSE entry ".
"to see if TLS is supoported: ".$rootDSE->getMessage(), $rootDSE->getCode());
}
$supported_extensions = $rootDSE->getValue('supportedExtension');
if (self::isError($supported_extensions)) {
return $this->raiseError("Unable to fetch rootDSE attribute 'supportedExtension' ".
"to see if TLS is supoported: ".$supported_extensions->getMessage(), $supported_extensions->getCode());
/* IGNORE this error, because server may refuse fetching the
RootDSE over an unencrypted connection. */
//return $this->raiseError("Unable to fetch rootDSE entry ".
//"to see if TLS is supoported: ".$rootDSE->getMessage(), $rootDSE->getCode());
} else {
/* Fetch suceeded, see, if the server supports TLS. Again, we
ignore errors, because the server may refuse to return
attributes over unencryted connections. */
$supported_extensions = $rootDSE->getValue('supportedExtension');
if (self::isError($supported_extensions)) {
/* IGNORE error, because server may refuse attribute
returning over an unencrypted connection. */
//return $this->raiseError("Unable to fetch rootDSE attribute 'supportedExtension' ".
//"to see if TLS is supoported: ".$supported_extensions->getMessage(), $supported_extensions->getCode());
} else {
// fetch succeedet, lets see if the server supports it.
// if not, then drop an error. If supported, then do nothing,
// because then we try to issue TLS afterwards.
if (!in_array('1.3.6.1.4.1.1466.20037', $supported_extensions)) {
return $this->raiseError("Server reports that it does not support TLS.");
}
}
}
if (in_array('1.3.6.1.4.1.1466.20037', $supported_extensions)) {
if (false === @ldap_start_tls($this->_link)) {
return $this->raiseError("TLS not started: " .
@ldap_error($this->_link),
@ldap_errno($this->_link));
}
return true;
// Try to establish TLS.
if (false === @ldap_start_tls($this->_link)) {
// Starting TLS failed. This may be an error, or because
// the server does not support it but did not enable us to
// detect that above.
return $this->raiseError("TLS could not be started: " .
@ldap_error($this->_link),
@ldap_errno($this->_link));
} else {
return $this->raiseError("Server reports that it does not support TLS");
return true; // TLS is started now.
}
}
......@@ -728,7 +745,7 @@ class Net_LDAP2 extends PEAR
// We have a failure. What type? We may be able to reconnect
// and try again.
$error_code = @ldap_errno($link);
$error_name = $this->errorMessage($error_code);
$error_name = Net_LDAP2::errorMessage($error_code);
if (($error_name === 'LDAP_OPERATIONS_ERROR') &&
($this->_config['auto_reconnect'])) {
......@@ -802,9 +819,9 @@ class Net_LDAP2 extends PEAR
// We have a failure. What type?
// We may be able to reconnect and try again.
$error_code = @ldap_errno($link);
$error_name = $this->errorMessage($error_code);
$error_name = Net_LDAP2::errorMessage($error_code);
if (($this->errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') &&
if ((Net_LDAP2::errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') &&
($this->_config['auto_reconnect'])) {
// The server has become disconnected before trying the
// operation. We should try again, possibly with a
......@@ -898,9 +915,9 @@ class Net_LDAP2 extends PEAR
// We have a failure. What type? We may be able to reconnect
// and try again.
$error_code = $msg->getCode();
$error_name = $this->errorMessage($error_code);
$error_name = Net_LDAP2::errorMessage($error_code);
if (($this->errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') &&
if ((Net_LDAP2::errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') &&
($this->_config['auto_reconnect'])) {
// The server has become disconnected before trying the
......@@ -937,9 +954,9 @@ class Net_LDAP2 extends PEAR
// We have a failure. What type? We may be able to reconnect
// and try again.
$error_code = $msg->getCode();
$error_name = $this->errorMessage($error_code);
$error_name = Net_LDAP2::errorMessage($error_code);
if (($this->errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') &&
if ((Net_LDAP2::errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') &&
($this->_config['auto_reconnect'])) {
// The server has become disconnected before trying the
......@@ -1078,14 +1095,14 @@ class Net_LDAP2 extends PEAR
return $obj = new Net_LDAP2_Search ($search, $this, $attributes);
} elseif ($err == 87) {
// bad search filter
return $this->raiseError($this->errorMessage($err) . "($filter)", $err);
return $this->raiseError(Net_LDAP2::errorMessage($err) . "($filter)", $err);
} elseif (($err == 1) && ($this->_config['auto_reconnect'])) {
// Errorcode 1 = LDAP_OPERATIONS_ERROR but we can try a reconnect.
$this->_link = false;
$this->performReconnect();
} else {
$msg = "\nParameters:\nBase: $base\nFilter: $filter\nScope: $scope";
return $this->raiseError($this->errorMessage($err) . $msg, $err);
return $this->raiseError(Net_LDAP2::errorMessage($err) . $msg, $err);
}
} else {
return $obj = new Net_LDAP2_Search($search, $this, $attributes);
......@@ -1114,7 +1131,7 @@ class Net_LDAP2 extends PEAR
$msg = @ldap_err2str($err);
} else {
$err = NET_LDAP2_ERROR;
$msg = $this->errorMessage($err);
$msg = Net_LDAP2::errorMessage($err);
}
return $this->raiseError($msg, $err);
}
......@@ -1146,7 +1163,7 @@ class Net_LDAP2 extends PEAR
$msg = @ldap_err2str($err);
} else {
$err = NET_LDAP2_ERROR;
$msg = $this->errorMessage($err);
$msg = Net_LDAP2::errorMessage($err);
}
return $this->raiseError($msg, $err);
}
......@@ -1239,30 +1256,21 @@ class Net_LDAP2 extends PEAR
return PEAR::raiseError('Parameter $dn is not a string nor an entry object!');
}
// make dn relative to parent
$base = Net_LDAP2_Util::ldap_explode_dn($dn, array('casefold' => 'none', 'reverse' => false, 'onlyvalues' => false));
if (self::isError($base)) {
return $base;
}
$entry_rdn = array_shift($base);
if (is_array($entry_rdn)) {
// maybe the dn consist of a multivalued RDN, we must build the dn in this case
// because the $entry_rdn is an array!
$filter_dn = Net_LDAP2_Util::canonical_dn($entry_rdn);
}
$base = Net_LDAP2_Util::canonical_dn($base);
// search LDAP for that DN by performing a baselevel search for any
// object. We can only find the DN in question this way, or nothing.
$s_opts = array(
'scope' => 'base',
'sizelimit' => 1,
'attributes' => '1.1' // select no attrs
);
$search = $this->search($dn, '(objectClass=*)', $s_opts);
$result = @ldap_list($this->_link, $base, $entry_rdn, array(), 1, 1);
if (@ldap_count_entries($this->_link, $result)) {
return true;
}
if (ldap_errno($this->_link) == 32) {
return false;
if (self::isError($search)) {
return $search;
}
if (ldap_errno($this->_link) != 0) {
return PEAR::raiseError(ldap_error($this->_link), ldap_errno($this->_link));
}
return false;
// retun wehter the DN exists; that is, we found an entry
return ($search->count() == 0)? false : true;
}
......@@ -1400,7 +1408,7 @@ class Net_LDAP2 extends PEAR
*
* @return string The errorstring for the error.
*/
public function errorMessage($errorcode)
public static function errorMessage($errorcode)
{
$errorMessages = array(
0x00 => "LDAP_SUCCESS",
......@@ -1629,7 +1637,7 @@ class Net_LDAP2 extends PEAR
}
/**
* Encodes given attributes to UTF8 if needed by schema
* Encodes given attributes from ISO-8859-1 to UTF-8 if needed by schema
*
* 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
......@@ -1650,7 +1658,7 @@ class Net_LDAP2 extends PEAR
}
/**
* Decodes the given attribute values if needed by schema
* Decodes the given attribute values from UTF-8 to ISO-8859-1 if needed by schema
*
* $attributes is expected to be an array with keys describing
* the attribute names and the values as the value of this attribute:
......@@ -1668,7 +1676,7 @@ class Net_LDAP2 extends PEAR
}
/**
* Encodes or decodes attribute values if needed
* Encodes or decodes UTF-8/ISO-8859-1 attribute values if needed by schema
*
* @param array $attributes Array of attributes
* @param array $function Function to apply to attribute values
......@@ -1701,7 +1709,10 @@ class Net_LDAP2 extends PEAR
continue;
}
if (false !== strpos($attr['syntax'], '1.3.6.1.4.1.1466.115.121.1.15')) {
// Encoding is needed if this is a DIR_STR. We assume also
// needed encoding in case the schema contains no syntax
// information (he does not need to, see rfc2252, 4.2)
if (!array_key_exists('syntax', $attr) || false !== strpos($attr['syntax'], '1.3.6.1.4.1.1466.115.121.1.15')) {
$encode = true;
} else {
$encode = false;
......
......@@ -12,7 +12,7 @@
* @author Benedikt Hallinger <beni@php.net>
* @copyright 2009 Tarjej Huse, Jan Wagner, Benedikt Hallinger
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
* @version SVN: $Id: Entry.php 286787 2009-08-04 06:03:12Z beni $
* @version SVN: $Id: Entry.php 332301 2013-12-09 08:17:14Z beni $
* @link http://pear.php.net/package/Net_LDAP2/
*/
......@@ -20,7 +20,7 @@
* Includes
*/
require_once 'PEAR.php';
require_once 'Util.php';
require_once 'Net/LDAP2/Util.php';
/**
* Object representation of a directory entry
......@@ -309,7 +309,7 @@ class Net_LDAP2_Entry extends PEAR
public function dn($dn = null)
{
if (false == is_null($dn)) {
if (is_null($this->_dn)) {
if (is_null($this->_dn) ) {
$this->_dn = $dn;
} else {
$this->_newdn = $dn;
......@@ -419,6 +419,7 @@ class Net_LDAP2_Entry extends PEAR
* The returned hash has the form
* <code>array('attributename' => 'single value',
* 'attributename' => array('value1', value2', value3'))</code>
* Only attributes present at the entry will be returned.
*
* @access public
* @return array Hash of all attributes with their values
......@@ -437,31 +438,59 @@ class Net_LDAP2_Entry extends PEAR
*
* The first parameter is the name of the attribute
* The second parameter influences the way the value is returned:
* 'single': only the first value is returned as string
* 'all': all values including the value count are returned in an
* array
* 'single': only the first value is returned as string
* 'all': all values are returned in an array
* 'default': in all other cases an attribute value with a single value is
* returned as string, if it has multiple values it is returned
* as an array (without value count)
* as an array
*
* @param string $attr Attribute name
* @param string $option Option
* If the attribute is not set at this entry (no value or not defined in
* schema), "false" is returned when $option is 'single', an empty string if
* 'default', and an empty array when 'all'.
*
* You may use Net_LDAP2_Schema->checkAttribute() to see if the attribute
* is defined for the objectClasses of this entry.
*
* @param string $attr Attribute name
* @param string $option Option
*
* @access public
* @return string|array|PEAR_Error string, array or PEAR_Error
* @return string|array
*/
public function getValue($attr, $option = null)
{
$attr = $this->getAttrName($attr);
if (false == array_key_exists($attr, $this->_attributes)) {
return PEAR::raiseError("Unknown attribute ($attr) requested");
}
$value = $this->_attributes[$attr];
// return depending on set $options
if (!array_key_exists($attr, $this->_attributes)) {
// attribute not set
switch ($option) {
case 'single':
$value = false;
break;
case 'all':
$value = array();
break;
default:
$value = '';
}
if ($option == "single" || (count($value) == 1 && $option != 'all')) {
$value = array_shift($value);
} else {
// attribute present
switch ($option) {
case 'single':
$value = $this->_attributes[$attr][0];
break;
case 'all':
$value = $this->_attributes[$attr];
break;
default:
$value = $this->_attributes[$attr];
if (count($value) == 1) {
$value = array_shift($value);
}
}
}
return $value;
......@@ -529,6 +558,9 @@ class Net_LDAP2_Entry extends PEAR
if (false == is_array($attr)) {
return PEAR::raiseError("Parameter must be an array");
}
if ($this->isNew()) {
$this->setAttributes($attr);
}
foreach ($attr as $k => $v) {
$k = $this->getAttrName($k);
if (false == is_array($v)) {
......@@ -547,11 +579,12 @@ class Net_LDAP2_Entry extends PEAR
$this->_attributes[$k] = $v;
}
// save changes for update()
if (empty($this->_changes["add"][$k])) {
if (!isset($this->_changes["add"][$k])) {
$this->_changes["add"][$k] = array();
}
$this->_changes["add"][$k] = array_unique(array_merge($this->_changes["add"][$k], $v));
}
$return = true;
return $return;
}
......@@ -760,6 +793,14 @@ class Net_LDAP2_Entry extends PEAR
$this->_changes['replace'] = array();
$this->_original = $this->_attributes;
// In case the "new" entry was moved after creation, we must
// adjust the internal DNs as the entry was already created
// with the most current DN.
if (false == is_null($this->_newdn)) {
$this->_dn = $this->_newdn;
$this->_newdn = null;
}
$return = true;
return $return;
}
......@@ -785,7 +826,8 @@ class Net_LDAP2_Entry extends PEAR
$parent = Net_LDAP2_Util::canonical_dn($parent);
// rename/move
if (false == @ldap_rename($link, $this->_dn, $child, $parent, true)) {
if (false == @ldap_rename($link, $this->_dn, $child, $parent, false)) {
return PEAR::raiseError("Entry not renamed: " .
@ldap_error($link), @ldap_errno($link));
}
......@@ -795,51 +837,55 @@ class Net_LDAP2_Entry extends PEAR
}
/*
* Carry out modifications to the entry
* Retrieve a entry that has all attributes we need so that the list of changes to build is created accurately
*/
$fullEntry = $ldap->getEntry( $this->dn() );
if ( Net_LDAP2::isError($fullEntry) ) {
return PEAR::raiseError("Could not retrieve a full set of attributes to reconcile changes with");
}
$modifications = array();
// ADD
foreach ($this->_changes["add"] as $attr => $value) {
// if attribute exists, add new values
if ($this->exists($attr)) {
if (false === @ldap_mod_add($link, $this->dn(), array($attr => $value))) {
return PEAR::raiseError("Could not add new values to attribute $attr: " .
@ldap_error($link), @ldap_errno($link));
}
} else {
// new attribute
if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) {
return PEAR::raiseError("Could not add new attribute $attr: " .
@ldap_error($link), @ldap_errno($link));
}
}
// all went well here, I guess
unset($this->_changes["add"][$attr]);
// if attribute exists, we need to combine old and new values
if ($fullEntry->exists($attr)) {
$currentValue = $fullEntry->getValue($attr, "all");
$value = array_merge( $currentValue, $value );
}
$modifications[$attr] = $value;
}
// DELETE
foreach ($this->_changes["delete"] as $attr => $value) {
// In LDAPv3 you need to specify the old values for deleting
if (is_null($value) && $ldap->getLDAPVersion() === 3) {
$value = $this->_original[$attr];
$value = $fullEntry->getValue($attr);
}
if (false === @ldap_mod_del($link, $this->dn(), array($attr => $value))) {
return PEAR::raiseError("Could not delete attribute $attr: " .
@ldap_error($link), @ldap_errno($link));
if (!is_array($value)) {
$value = array($value);
}
unset($this->_changes["delete"][$attr]);
// Find out what is missing from $value and exclude it
$currentValue = isset($modifications[$attr]) ? $modifications[$attr] : $fullEntry->getValue($attr, "all");
$modifications[$attr] = array_values( array_diff( $currentValue, $value ) );
}
// REPLACE
foreach ($this->_changes["replace"] as $attr => $value) {
if (false === @ldap_modify($link, $this->dn(), array($attr => $value))) {
return PEAR::raiseError("Could not replace attribute $attr values: " .
@ldap_error($link), @ldap_errno($link));
}
unset($this->_changes["replace"][$attr]);
$modifications[$attr] = $value;
}
// all went well, so _original (server) becomes _attributes (local copy)
$this->_original = $this->_attributes;
// COMMIT
if (false === @ldap_modify($link, $this->dn(), $modifications)) {
return PEAR::raiseError("Could not modify the entry: " . @ldap_error($link), @ldap_errno($link));
}
// all went well, so _original (server) becomes _attributes (local copy), reset _changes too...
$this->_changes['add'] = array();
$this->_changes['delete'] = array();
$this->_changes['replace'] = array();
$this->_original = $this->_attributes;
$return = true;
return $return;
......@@ -964,11 +1010,6 @@ class Net_LDAP2_Entry extends PEAR
// fetch attribute values
$attr = $this->getValue($attr_name, 'all');
if (Net_LDAP2::isError($attr)) {
return $attr;
} else {
unset($attr['count']);
}
// perform preg_match() on all values
$match = false;
......
......@@ -10,7 +10,7 @@
* @author Benedikt Hallinger <beni@php.net>
* @copyright 2009 Benedikt Hallinger
* @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
* @version SVN: $Id: Filter.php 289978 2009-10-27 09:56:41Z beni $
* @version SVN: $Id: Filter.php 332305 2013-12-09 08:51:41Z beni $
* @link http://pear.php.net/package/Net_LDAP2/
*/
......@@ -18,7 +18,8 @@
* Includes
*/
require_once 'PEAR.php';
require_once 'Util.php';
require_once 'Net/LDAP2/Util.php';
require_once 'Net/LDAP2/Entry.php';
/**
* Object representation of a part of a LDAP filter.
......@@ -134,6 +135,9 @@ class Net_LDAP2_Filter extends PEAR
* - lessOrEqual: The attributes value is less or equal than $value
* - approx: One of the attributes values is similar to $value
*
* Negation ("not") can be done by prepending the above operators with the
* "not" or "!" keyword, see example below.
*
* If $escape is set to true (default) then $value will be escaped
* properly. If it is set to false then $value will be treaten as raw filter value string.
* You should escape yourself using {@link Net_LDAP2_Util::escape_filter_value()}!
......@@ -141,10 +145,13 @@ class Net_LDAP2_Filter extends PEAR
* Examples:
* <code>
* // This will find entries that contain an attribute "sn" that ends with "foobar":
* $filter = new Net_LDAP2_Filter('sn', 'ends', 'foobar');
* $filter = Net_LDAP2_Filter::create('sn', 'ends', 'foobar');
*
* // This will find entries that contain an attribute "sn" that has any value set:
* $filter = new Net_LDAP2_Filter('sn', 'any');
* $filter = Net_LDAP2_Filter::create('sn', 'any');
*
* // This will build a negated equals filter:
* $filter = Net_LDAP2_Filter::create('sn', 'not equals', 'foobar');
* </code>
*
* @param string $attr_name Name of the attribute the filter should apply to
......@@ -161,8 +168,22 @@ class Net_LDAP2_Filter extends PEAR
$array = Net_LDAP2_Util::escape_filter_value(array($value));
$value = $array[0];
}
switch (strtolower($match)) {
$match = strtolower($match);
// detect negation
$neg_matches = array();
$negate_filter = false;
if (preg_match('/^(?:not|!)[\s_-](.+)/', $match, $neg_matches)) {
$negate_filter = true;
$match = $neg_matches[1];
}
// build basic filter
switch ($match) {
case 'equals':
case '=':
case '==':
$leaf_filter->_filter = '(' . $attr_name . '=' . $value . ')';
break;
case 'begins':
......@@ -175,9 +196,11 @@ class Net_LDAP2_Filter extends PEAR
$leaf_filter->_filter = '(' . $attr_name . '=*' . $value . '*)';
break;
case 'greater':
case '>':
$leaf_filter->_filter = '(' . $attr_name . '>' . $value . ')';
break;
case 'less':
case '<':
$leaf_filter->_filter = '(' . $attr_name . '<' . $value . ')';
break;
case 'greaterorequal':
......@@ -199,6 +222,12 @@ class Net_LDAP2_Filter extends PEAR
default:
return PEAR::raiseError('Net_LDAP2_Filter create error: matching rule "' . $match . '" not known!');
}
// negate if requested
if ($negate_filter) {
$leaf_filter = Net_LDAP2_Filter::combine('!', $leaf_filter);
}
return $leaf_filter;
}
......@@ -207,10 +236,10 @@ class Net_LDAP2_Filter extends PEAR
*
* This static method combines two or more filter objects and returns one single
* filter object that contains all the others.
* Call this method statically: $filter = Net_LDAP2_Filter('or', array($filter1, $filter2))
* Call this method statically: $filter = Net_LDAP2_Filter::combine('or', array($filter1, $filter2))
* If the array contains filter strings instead of filter objects, we will try to parse them.
*
* @param string $log_op The locicall operator. May be "and", "or", "not" or the subsequent logical equivalents "&", "|", "!"
* @param string $log_op The locical operator. May be "and", "or", "not" or the subsequent logical equivalents "&", "|", "!"
* @param array|Net_LDAP2_Filter $filters array with Net_LDAP2_Filter objects
*
* @return Net_LDAP2_Filter|Net_LDAP2_Error
......@@ -241,8 +270,13 @@ class Net_LDAP2_Filter extends PEAR
$filters = array($filter_o);
}
} elseif (is_array($filters)) {
$err = PEAR::raiseError('Net_LDAP2_Filter combine error: operator is "not" but $filter is an array!');
return $err;
if (count($filters) != 1) {
$err = PEAR::raiseError('Net_LDAP2_Filter combine error: operator is "not" but $filter is an array!');