diff --git a/lib/Net/LDAP3.php b/lib/Net/LDAP3.php index badedb5d2ac88d215a9a4593483c54ab6e5ee6d1..2662e915f3643db7aa224c1bf8587b619537c2d1 100644 --- a/lib/Net/LDAP3.php +++ b/lib/Net/LDAP3.php @@ -44,7 +44,7 @@ class Net_LDAP3 const CONTROL_EFFECTIVE_RIGHTS = '1.3.6.1.4.1.42.2.27.9.5.2'; const CONTROL_SORT_REQUEST = '1.2.840.113556.1.4.473'; const CONTROL_VLV_REQUEST = '2.16.840.1.113730.3.4.9'; - + const CONTROL_VLV_RESPONSE = '2.16.840.1.113730.3.4.10'; public $conn; public $vlv_active = false; @@ -1625,14 +1625,17 @@ class Net_LDAP3 } $this->_debug("D: total numsubordinates = " . $vlv_count); } - // ...or by fetching all records dn and count them - else if (!function_exists('ldap_parse_virtuallist_control')) { + // ...or by parsing the controls in the response, and if that's not supported + // by fetching all records dn and counting them + else if (PHP_VERSION_ID < 70305 && !function_exists('ldap_parse_virtuallist_control')) { // @FIXME: this search will ignore $props['search'] $vlv_count = $this->search($base_dn, $filter, $scope, array('dn'), $props, true); } - $this->vlv_active = $this->_vlv_set_controls($sort, $this->list_page, $this->page_size, + $controls = $this->_vlv_set_controls($sort, $this->list_page, $this->page_size, $this->_vlv_search($sort, $props['search'])); + + $this->vlv_active = (bool) $controls; } else { $this->vlv_active = false; @@ -1668,18 +1671,32 @@ class Net_LDAP3 $this->_debug("Executing search with return attributes: " . var_export($attrs, true)); - $ldap_result = @$function($this->conn, $base_dn, $filter, $attrs, 0, $sizelimit, $timelimit); + if (is_array($controls)) { + $ldap_result = $function($this->conn, $base_dn, $filter, $attrs, 0, $sizelimit, $timelimit, LDAP_DEREF_NEVER, $controls); + } + else { + $ldap_result = @$function($this->conn, $base_dn, $filter, $attrs, 0, $sizelimit, $timelimit); + } if (!$ldap_result) { $this->_warning("LDAP: $function failed for dn=$base_dn. " . ldap_error($this->conn)); return false; } - // when running on a patched PHP we can use the extended functions - // to retrieve the total count from the LDAP search result - if ($this->vlv_active && function_exists('ldap_parse_virtuallist_control')) { + // when running on a PHP with server controls support we can + // retrieve the total count from the LDAP search result + if ($this->vlv_active && (is_array($controls) || function_exists('ldap_parse_virtuallist_control'))) { if (ldap_parse_result($this->conn, $ldap_result, $errcode, $matcheddn, $errmsg, $referrals, $serverctrls)) { - ldap_parse_virtuallist_control($this->conn, $serverctrls, $last_offset, $vlv_count, $vresult); + if (PHP_VERSION_ID >= 70300) { + $vlv_count = (int) $serverctrls[self::CONTROL_VLV_RESPONSE]['value']['count']; + // FIXME: I don't know this is the same offset value as in ldap_parse_virtuallist_control() below + // but anyway it looks like we do not use that value for anything + $last_offset = (int) $serverctrls[self::CONTROL_VLV_RESPONSE]['value']['target']; + } + else { + ldap_parse_virtuallist_control($this->conn, $serverctrls, $last_offset, $vlv_count, $vresult); + } + $this->_debug("S: VLV result: last_offset=$last_offset; content_count=$vlv_count"); } else { @@ -2598,39 +2615,6 @@ class Net_LDAP3 } } - /** - * Add BER sequence with correct length and the given identifier - */ - private static function _ber_addseq($str, $identifier) - { - $len = dechex(strlen($str)/2); - if (strlen($len) % 2 != 0) { - $len = '0'.$len; - } - - return $identifier . $len . $str; - } - - /** - * Returns BER encoded integer value in hex format - */ - private static function _ber_encode_int($offset) - { - $val = dechex($offset); - $prefix = ''; - - // check if bit 8 of high byte is 1 - if (preg_match('/^[89abcdef]/', $val)) { - $prefix = '00'; - } - - if (strlen($val)%2 != 0) { - $prefix .= '0'; - } - - return $prefix . $val; - } - /** * Quotes attribute value string * @@ -2705,6 +2689,107 @@ class Net_LDAP3 return implode(',', $result); } + private function _fuzzy_search_prefix() + { + switch ($this->config_get("fuzzy_search", 2)) { + case 2: + return "*"; + break; + case 1: + case 0: + default: + return ""; + break; + } + } + + private function _fuzzy_search_suffix() + { + switch ($this->config_get("fuzzy_search", 2)) { + case 2: + return "*"; + break; + case 1: + return "*"; + case 0: + default: + return ""; + break; + } + } + + /** + * Return the search string value to be used in VLV controls + * + * @param array $sort List of attributes in vlv index + * @param array|string $search Search string or attribute => value hash + * + * @return string Search string + */ + private function _vlv_search($sort, $search) + { + if (!empty($this->additional_filter)) { + $this->_debug("Not setting a VLV search filter because we already have a filter"); + return; + } + + if (empty($search)) { + return; + } + + foreach ((array) $search as $attr => $value) { + if ($attr && !in_array(strtolower($attr), $sort)) { + $this->_debug("Cannot use VLV search using attribute not indexed: $attr (not in " . var_export($sort, true) . ")"); + return; + } + else { + return $value . $this->_fuzzy_search_suffix(); + } + } + } + + /** + * Set server controls for Virtual List View (paginated listing) + */ + private function _vlv_set_controls($sort, $list_page, $page_size, $search = null) + { + $sort_ctrl = array( + 'oid' => self::CONTROL_SORT_REQUEST, + 'value' => self::_sort_ber_encode($sort) + ); + + if (!empty($search)) { + $this->_debug("_vlv_set_controls to include search: " . var_export($search, true)); + } + + $vlv_ctrl = array( + 'oid' => self::CONTROL_VLV_REQUEST, + 'value' => self::_vlv_ber_encode( + $offset = ($list_page-1) * $page_size + 1, + $page_size, + $search + ), + 'iscritical' => true + ); + + $this->_debug("C: set controls sort=" . join(' ', unpack('H'.(strlen($sort_ctrl['value'])*2), $sort_ctrl['value'])) + . " (" . implode(',', (array) $sort) . ");" + . " vlv=" . join(' ', (unpack('H'.(strlen($vlv_ctrl['value'])*2), $vlv_ctrl['value']))) . " ($offset/$page_size)"); + + $controls = array($sort_ctrl, $vlv_ctrl); + + if (PHP_VERSION_ID >= 70305) { + return $controls; + } + + if (!ldap_set_option($this->conn, LDAP_OPT_SERVER_CONTROLS, $controls)) { + $this->_debug("S: ".ldap_error($this->conn)); + return false; + } + + return true; + } + /** * create ber encoding for sort control * @@ -2729,14 +2814,6 @@ class Net_LDAP3 return pack('H'.strlen($str), $str); } - /** - * Returns ascii string encoded in hex - */ - private static function _string2hex($str) - { - return implode(unpack("H*", $str)); - } - /** * Generate BER encoded string for Virtual List View option * @@ -2799,101 +2876,45 @@ class Net_LDAP3 return pack('H'.strlen($str), $str); } - private function _fuzzy_search_prefix() + /** + * Add BER sequence with correct length and the given identifier + */ + private static function _ber_addseq($str, $identifier) { - switch ($this->config_get("fuzzy_search", 2)) { - case 2: - return "*"; - break; - case 1: - case 0: - default: - return ""; - break; + $len = dechex(strlen($str)/2); + if (strlen($len) % 2 != 0) { + $len = '0'.$len; } - } - private function _fuzzy_search_suffix() - { - switch ($this->config_get("fuzzy_search", 2)) { - case 2: - return "*"; - break; - case 1: - return "*"; - case 0: - default: - return ""; - break; - } + return $identifier . $len . $str; } /** - * Return the search string value to be used in VLV controls - * - * @param array $sort List of attributes in vlv index - * @param array|string $search Search string or attribute => value hash - * - * @return string Search string + * Returns BER encoded integer value in hex format */ - private function _vlv_search($sort, $search) + private static function _ber_encode_int($offset) { - if (!empty($this->additional_filter)) { - $this->_debug("Not setting a VLV search filter because we already have a filter"); - return; - } + $val = dechex($offset); + $prefix = ''; - if (empty($search)) { - return; + // check if bit 8 of high byte is 1 + if (preg_match('/^[89abcdef]/', $val)) { + $prefix = '00'; } - foreach ((array) $search as $attr => $value) { - if ($attr && !in_array(strtolower($attr), $sort)) { - $this->_debug("Cannot use VLV search using attribute not indexed: $attr (not in " . var_export($sort, true) . ")"); - return; - } - else { - return $value . $this->_fuzzy_search_suffix(); - } + if (strlen($val)%2 != 0) { + $prefix .= '0'; } + + return $prefix . $val; } /** - * Set server controls for Virtual List View (paginated listing) + * Returns ascii string encoded in hex */ - private function _vlv_set_controls($sort, $list_page, $page_size, $search = null) + private static function _string2hex($str) { - $sort_ctrl = array( - 'oid' => self::CONTROL_SORT_REQUEST, - 'value' => self::_sort_ber_encode($sort) - ); - - if (!empty($search)) { - $this->_debug("_vlv_set_controls to include search: " . var_export($search, true)); - } - - $vlv_ctrl = array( - 'oid' => self::CONTROL_VLV_REQUEST, - 'value' => self::_vlv_ber_encode( - $offset = ($list_page-1) * $page_size + 1, - $page_size, - $search - ), - 'iscritical' => true - ); - - $this->_debug("C: set controls sort=" . join(' ', unpack('H'.(strlen($sort_ctrl['value'])*2), $sort_ctrl['value'])) - . " (" . implode(',', (array) $sort) . ");" - . " vlv=" . join(' ', (unpack('H'.(strlen($vlv_ctrl['value'])*2), $vlv_ctrl['value']))) . " ($offset/$page_size)"); - - if (!ldap_set_option($this->conn, LDAP_OPT_SERVER_CONTROLS, array($sort_ctrl, $vlv_ctrl))) { - $this->_debug("S: ".ldap_error($this->conn)); - $this->set_error(self::ERROR_SEARCH, 'vlvnotsupported'); - - return false; - } - - return true; + return implode(unpack("H*", $str)); } /** diff --git a/lib/Net/LDAP3/Result.php b/lib/Net/LDAP3/Result.php index 9b428cfc3605a78787c1923da3a9c9c11dd94ef0..ef57cc8f189bcaa46a6da6b9fa0c78fae2434440 100644 --- a/lib/Net/LDAP3/Result.php +++ b/lib/Net/LDAP3/Result.php @@ -42,6 +42,7 @@ class Net_LDAP3_Result implements Iterator protected $base_dn; protected $filter; protected $scope; + protected $result; private $count; private $current;