public function __construct(Ldap $link, $result = null) { $this->result = $result; if (is_resource($result)) { // Get the status code, matched DN and referrals from the response ldap_parse_result($link->resource(), $result, $this->code, $this->matchedDN, $this->message, $this->referrals); // Get the string representation of the status code $this->message = ldap_err2str($this->code); // Extract the data from the resource $this->data = ldap_get_entries($link->resource(), $result); $this->data = $this->cleanup_result($this->data); // Remove the referrals array if there's nothing inside count($this->referrals) == 0 && ($this->referrals = null); // Try to extract pagination cookie and estimated number of objects to be returned // Since there's no way to tell if pagination has been enabled or not, I am suppressing php errors @ldap_control_paged_result_response($link->resource(), $result, $this->cookie, $this->estimated); } else { $this->code = ldap_errno($link->resource()); $this->message = ldap_error($link->resource()); } // Active Directory conceals some additional error codes in the ErrorMessage of the response // that we cannot get to with ldap_errno() in authentication failures - let's try to // extract them! if ($this->code == 49) { $message = null; ldap_get_option($link->resource(), Option::ErrorString, $message); if (stripos($message, 'AcceptSecurityContext') !== false) { $message = explode(', ', $message); end($message); $message = prev($message); $this->code = explode(' ', $message)[1]; // For compatibility reasons with standard ldap, if the error code // is 52e let's replace it with 49 ( their meanings are equal, it's just // Microsoft doing it its own way again ) if ($this->code == '52e') { $this->code = ResponseCode::InvalidCredentials; } } } }
<?php require "connect.inc"; $link = ldap_connect_and_bind($host, $port, $user, $passwd, $protocol_version); insert_dummy_data($link); ldap_add($link, "cn=userref,dc=my-domain,dc=com", array("objectClass" => array("extensibleObject", "referral"), "cn" => "userref", "ref" => "cn=userA,dc=my-domain,dc=com")); $result = ldap_search($link, "cn=userref,dc=my-domain,dc=com", "(cn=user*)"); $errcode = $dn = $errmsg = $refs = null; var_dump(ldap_parse_result($link, $result, $errcode, $dn, $errmsg, $refs), $errcode, $dn, $errmsg, $refs); ?> ===DONE=== <?php include "connect.inc"; $link = ldap_connect_and_bind($host, $port, $user, $passwd, $protocol_version); // Referral can only be removed with Manage DSA IT Control ldap_set_option($link, LDAP_OPT_SERVER_CONTROLS, array(array("oid" => "2.16.840.1.113730.3.4.2"))); ldap_delete($link, "cn=userref,dc=my-domain,dc=com"); remove_dummy_data($link);
/** * Execute the LDAP search based on the stored credentials */ private function _exec_search($count = false) { if ($this->ready) { $filter = $this->filter ? $this->filter : '(objectclass=*)'; $function = $this->_scope2func($this->prop['scope'], $ns_function); $this->_debug("C: Search [{$filter}][dn: {$this->base_dn}]"); // when using VLV, we get the total count by... if (!$count && $function != 'ldap_read' && $this->prop['vlv'] && !$this->group_id) { // ...either reading numSubOrdinates attribute if ($this->prop['numsub_filter'] && ($result_count = @$ns_function($this->conn, $this->base_dn, $this->prop['numsub_filter'], array('numSubOrdinates'), 0, 0, 0))) { $counts = ldap_get_entries($this->conn, $result_count); for ($this->vlv_count = $j = 0; $j < $counts['count']; $j++) { $this->vlv_count += $counts[$j]['numsubordinates'][0]; } $this->_debug("D: total numsubordinates = " . $this->vlv_count); } else { if (!function_exists('ldap_parse_virtuallist_control')) { // ...or by fetching all records dn and count them $this->vlv_count = $this->_exec_search(true); } } $this->vlv_active = $this->_vlv_set_controls($this->prop, $this->list_page, $this->page_size); } // only fetch dn for count (should keep the payload low) $attrs = $count ? array('dn') : array_values($this->fieldmap); if ($this->ldap_result = @$function($this->conn, $this->base_dn, $filter, $attrs, 0, (int) $this->prop['sizelimit'], (int) $this->prop['timelimit'])) { // 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')) { if (ldap_parse_result($this->conn, $this->ldap_result, $errcode, $matcheddn, $errmsg, $referrals, $serverctrls)) { ldap_parse_virtuallist_control($this->conn, $serverctrls, $last_offset, $this->vlv_count, $vresult); $this->_debug("S: VLV result: last_offset={$last_offset}; content_count={$this->vlv_count}"); } else { $this->_debug("S: " . ($errmsg ? $errmsg : ldap_error($this->conn))); } } $entries_count = ldap_count_entries($this->conn, $this->ldap_result); $this->_debug("S: {$entries_count} record(s)"); return $count ? $entries_count : true; } else { $this->_debug("S: " . ldap_error($this->conn)); } } return false; }
/** * Execute LDAP search * * @param string $base_dn Base DN to use for searching * @param string $filter Filter string to query * @param string $scope The LDAP scope (list|sub|base) * @param array $attrs List of entry attributes to read * @param array $prop Hash array with query configuration properties: * - sort: array of sort attributes (has to be in sync with the VLV index) * - search: search string used for VLV controls * @param bool $count_only Set to true if only entry count is requested * * @return mixed Net_LDAP3_Result object or number of entries (if $count_only=true) or False on failure */ public function search($base_dn, $filter = '(objectclass=*)', $scope = 'sub', $attrs = array('dn'), $props = array(), $count_only = false) { if (!$this->conn) { $this->_debug("No active connection for " . __CLASS__ . "::" . __FUNCTION__); return false; } // make sure attributes list is not empty if (empty($attrs)) { $attrs = array('dn'); } // make sure filter is not empty if (empty($filter)) { $filter = '(objectclass=*)'; } $this->_debug("C: Search base dn: [{$base_dn}] scope [{$scope}] with filter [{$filter}]"); $function = self::scope_to_function($scope, $ns_function); if (!$count_only && ($sort = $this->find_vlv($base_dn, $filter, $scope, $props['sort']))) { // when using VLV, we get the total count by... // ...either reading numSubOrdinates attribute if (($sub_filter = $this->config_get('numsub_filter')) && ($result_count = @$ns_function($this->conn, $base_dn, $sub_filter, array('numSubOrdinates'), 0, 0, 0))) { $counts = ldap_get_entries($this->conn, $result_count); for ($vlv_count = $j = 0; $j < $counts['count']; $j++) { $vlv_count += $counts[$j]['numsubordinates'][0]; } $this->_debug("D: total numsubordinates = " . $vlv_count); } else { if (!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, $this->_vlv_search($sort, $props['search'])); } else { $this->vlv_active = false; } $sizelimit = (int) $this->config['sizelimit']; $timelimit = (int) $this->config['timelimit']; $phplimit = (int) @ini_get('max_execution_time'); // set LDAP time limit to be (one second) less than PHP time limit // otherwise we have no chance to log the error below if ($phplimit && $timelimit >= $phplimit) { $timelimit = $phplimit - 1; } $this->_debug("Using function {$function} on scope {$scope} (\$ns_function is {$ns_function})"); if ($this->vlv_active) { if (!empty($this->additional_filter)) { $filter = "(&" . $filter . $this->additional_filter . ")"; $this->_debug("C: (With VLV) Setting a filter (with additional filter) of " . $filter); } else { $this->_debug("C: (With VLV) Setting a filter (without additional filter) of " . $filter); } } else { if (!empty($this->additional_filter)) { $filter = "(&" . $filter . $this->additional_filter . ")"; } $this->_debug("C: (Without VLV) Setting a filter of " . $filter); } $this->_debug("Executing search with return attributes: " . var_export($attrs, true)); $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')) { 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); $this->_debug("S: VLV result: last_offset={$last_offset}; content_count={$vlv_count}"); } else { $this->_debug("S: " . ($errmsg ? $errmsg : ldap_error($this->conn))); } } else { $this->_debug("S: " . ldap_count_entries($this->conn, $ldap_result) . " record(s) found"); } $result = new Net_LDAP3_Result($this->conn, $base_dn, $filter, $scope, $ldap_result); if (isset($last_offset)) { $result->set('offset', $last_offset); } if (isset($vlv_count)) { $result->set('count', $vlv_count); } $result->set('vlv', $this->vlv_active); return $count_only ? $result->count() : $result; }
protected function _parse_referrals($result_id) { if (!ldap_parse_result($this->adxLink->get_link(), $result_id, $code, $matchedDN, $errMsg, $referrals)) { return; } if (count($referrals) == 0) { return; } // Referral looks like this: // ldap://example.com/DC=example,DC=com // // Extract the protocol and url from the referral string if (preg_match("/^ldaps?:\\/\\/[a-zA-Z0-9_\\-.]*/", $referrals[0], $matches) !== 1) { throw new Exception("Unable to parse referral - {$referrals[0]}"); } // Match found? Return it! if (isset($matches[0])) { return $matches[0]; } }
/** * Execute the LDAP search based on the stored credentials * * @param string $base_dn The base DN to query * @param string $filter The LDAP filter for search * @param string $scope The LDAP scope (list|sub|base) * @param array $attrs List of entry attributes to read * @param array $prop Hash array with query configuration properties: * - sort: array of sort attributes (has to be in sync with the VLV index) * - search: search string used for VLV controls * @param boolean $count_only Set to true if only entry count is requested * * @return mixed rcube_ldap_result object or number of entries (if count_only=true) or false on error */ public function search($base_dn, $filter = '', $scope = 'sub', $attrs = array('dn'), $prop = array(), $count_only = false) { if (!$this->conn) { return false; } if (empty($filter)) { $filter = '(objectclass=*)'; } $this->_debug("C: Search {$base_dn} for {$filter}"); $function = self::scope2func($scope, $ns_function); // find available VLV index for this query if (!$count_only && ($vlv_sort = $this->_find_vlv($base_dn, $filter, $scope, $prop['sort']))) { // when using VLV, we get the total count by... // ...either reading numSubOrdinates attribute if (($sub_filter = $this->config['numsub_filter']) && ($result_count = @$ns_function($this->conn, $base_dn, $sub_filter, array('numSubOrdinates'), 0, 0, 0))) { $counts = ldap_get_entries($this->conn, $result_count); for ($vlv_count = $j = 0; $j < $counts['count']; $j++) { $vlv_count += $counts[$j]['numsubordinates'][0]; } $this->_debug("D: total numsubordinates = " . $vlv_count); } else { if (!function_exists('ldap_parse_virtuallist_control')) { $vlv_count = $this->search($base_dn, $filter, $scope, array('dn'), $prop, true); } } $this->vlv_active = $this->_vlv_set_controls($vlv_sort, $this->list_page, $this->page_size, $prop['search']); } else { $this->vlv_active = false; } // only fetch dn for count (should keep the payload low) if ($ldap_result = @$function($this->conn, $base_dn, $filter, $attrs, 0, (int) $this->config['sizelimit'], (int) $this->config['timelimit'])) { // 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')) { 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); $this->_debug("S: VLV result: last_offset={$last_offset}; content_count={$vlv_count}"); } else { $this->_debug("S: " . ($errmsg ? $errmsg : ldap_error($this->conn))); } } else { if ($this->debug) { $this->_debug("S: " . ldap_count_entries($this->conn, $ldap_result) . " record(s) found"); } } $this->result = new rcube_ldap_result($this->conn, $ldap_result, $base_dn, $filter, $vlv_count); return $count_only ? $this->result->count() : $this->result; } else { $this->_debug("S: " . ldap_error($this->conn)); } return false; }
/** * @link http://php.net/manual/en/function.ldap-parse-result.php * @param $link * @param $result * @param $errcode * @param null $matcheddn * @param null $errmsg * @param array|null $referrals * @return bool */ public function parseResult($link, $result, &$errcode, &$matcheddn = null, &$errmsg = null, array &$referrals = null) { return ldap_parse_result($link, $result, $errcode, $matcheddn, $errmsg, $referrals); }
<?php require "connect.inc"; var_dump(ldap_parse_result(null, null)); ?> ===DONE===
/** * @return array * @throws InformationRetrievalFailureException */ public function parse() { if (!ldap_parse_result($this->link, $this->result, $errCode, $matchedDN, $errMsg, $referrals)) { throw new InformationRetrievalFailureException(ldap_error($this->link), ldap_errno($this->link)); } return ['errcode' => $errCode, 'errmsg' => $errMsg, 'matcheddn' => $matchedDN, 'referrals' => $referrals]; }