/** * Perform a LDAP search and return the result * * @param LdapQuery $query * @param array $attributes An array of the required attributes * @param int $attrsonly Should be set to 1 if only attribute types are wanted * @param int $sizelimit Enables you to limit the count of entries fetched * @param int $timelimit Sets the number of seconds how long is spend on the search * @param int $deref * * @return resource|bool A search result identifier or false on error * * @throws LogicException If the LDAP query search scope is unsupported */ public function ldapSearch(LdapQuery $query, array $attributes = null, $attrsonly = 0, $sizelimit = 0, $timelimit = 0, $deref = LDAP_DEREF_NEVER) { $queryString = (string) $query; $baseDn = $query->getBase() ?: $this->getDn(); $scope = $query->getScope(); if (Logger::getInstance()->getLevel() === Logger::DEBUG) { // We're checking the level by ourself to avoid rendering the ldapsearch commandline for nothing $starttlsParam = $this->encryption === static::STARTTLS ? ' -ZZ' : ''; $ldapUrl = ($this->encryption === static::LDAPS ? 'ldaps://' : 'ldap://') . $this->hostname . ($this->port ? ':' . $this->port : ''); if ($this->bound) { $bindParams = ' -D "' . $this->bindDn . '"' . ($this->bindPw ? ' -W' : ''); } if ($deref === LDAP_DEREF_NEVER) { $derefName = 'never'; } elseif ($deref === LDAP_DEREF_ALWAYS) { $derefName = 'always'; } elseif ($deref === LDAP_DEREF_SEARCHING) { $derefName = 'search'; } else { // $deref === LDAP_DEREF_FINDING $derefName = 'find'; } Logger::debug("Issueing LDAP search. Use '%s' to reproduce.", sprintf('ldapsearch -P 3%s -H "%s"%s -b "%s" -s "%s" -z %u -l %u -a "%s"%s%s%s', $starttlsParam, $ldapUrl, $bindParams, $baseDn, $scope, $sizelimit, $timelimit, $derefName, $attrsonly ? ' -A' : '', $queryString ? ' "' . $queryString . '"' : '', $attributes ? ' "' . join('" "', $attributes) . '"' : '')); } switch ($scope) { case LdapQuery::SCOPE_SUB: $function = 'ldap_search'; break; case LdapQuery::SCOPE_ONE: $function = 'ldap_list'; break; case LdapQuery::SCOPE_BASE: $function = 'ldap_read'; break; default: throw new LogicException('LDAP scope %s not supported by ldapSearch', $scope); } return @$function($this->getConnection(), $baseDn, $queryString, $attributes, $attrsonly, $sizelimit, $timelimit, $deref); }
/** * Run the given LDAP query and return the resulting entries * * This utilizes paged search requests as defined in RFC 2696. * * @param LdapQuery $query The query to fetch results with * @param array $fields Request these attributes instead of the ones registered in the given query * @param int $pageSize The maximum page size, defaults to self::PAGE_SIZE * * @return array * * @throws LdapException In case an error occured while fetching the results */ protected function runPagedQuery(LdapQuery $query, array $fields = null, $pageSize = null) { if ($pageSize === null) { $pageSize = static::PAGE_SIZE; } $limit = $query->getLimit(); $offset = $query->hasOffset() ? $query->getOffset() - 1 : 0; $queryString = (string) $query; $base = $query->getBase() ?: $this->rootDn; if ($fields === null) { $fields = $query->getColumns(); } $ds = $this->getConnection(); $serverSorting = false; //$this->capabilities->hasOid(Capability::LDAP_SERVER_SORT_OID); if ($serverSorting && $query->hasOrder()) { ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array(array('oid' => LdapCapabilities::LDAP_SERVER_SORT_OID, 'value' => $this->encodeSortRules($query->getOrder())))); } elseif ($query->hasOrder()) { foreach ($query->getOrder() as $rule) { if (!in_array($rule[0], $fields)) { $fields[] = $rule[0]; } } } $count = 0; $cookie = ''; $entries = array(); do { // Do not request the pagination control as a critical extension, as we want the // server to return results even if the paged search request cannot be satisfied ldap_control_paged_result($ds, $pageSize, false, $cookie); $results = @ldap_search($ds, $base, $queryString, array_values($fields), 0, $serverSorting && $limit ? $offset + $limit : 0); if ($results === false) { if (ldap_errno($ds) === self::LDAP_NO_SUCH_OBJECT) { break; } throw new LdapException('LDAP query "%s" (base %s) failed. Error: %s', $queryString, $base, ldap_error($ds)); } elseif (ldap_count_entries($ds, $results) === 0) { if (in_array(ldap_errno($ds), array(static::LDAP_SIZELIMIT_EXCEEDED, static::LDAP_ADMINLIMIT_EXCEEDED))) { Logger::warning('Unable to request more than %u results. Does the server allow paged search requests? (%s)', $count, ldap_error($ds)); } break; } $entry = ldap_first_entry($ds, $results); do { $count += 1; if (!$serverSorting || $offset === 0 || $offset < $count) { $entries[ldap_get_dn($ds, $entry)] = $this->cleanupAttributes(ldap_get_attributes($ds, $entry), array_flip($fields)); } } while ((!$serverSorting || $limit === 0 || $limit !== count($entries)) && ($entry = ldap_next_entry($ds, $entry))); if (false === @ldap_control_paged_result_response($ds, $results, $cookie)) { // If the page size is greater than or equal to the sizeLimit value, the server should ignore the // control as the request can be satisfied in a single page: https://www.ietf.org/rfc/rfc2696.txt // This applies no matter whether paged search requests are permitted or not. You're done once you // got everything you were out for. if ($serverSorting && count($entries) !== $limit) { // The server does not support pagination, but still returned a response by ignoring the // pagedResultsControl. We output a warning to indicate that the pagination control was ignored. Logger::warning('Unable to request paged LDAP results. Does the server allow paged search requests?'); } } ldap_free_result($results); } while ($cookie && (!$serverSorting || $limit === 0 || count($entries) < $limit)); if ($cookie) { // A sequence of paged search requests is abandoned by the client sending a search request containing a // pagedResultsControl with the size set to zero (0) and the cookie set to the last cookie returned by // the server: https://www.ietf.org/rfc/rfc2696.txt ldap_control_paged_result($ds, 0, false, $cookie); ldap_search($ds, $base, $queryString); // Returns no entries, due to the page size } else { // Reset the paged search request so that subsequent requests succeed ldap_control_paged_result($ds, 0); } if (!$serverSorting && $query->hasOrder()) { uasort($entries, array($query, 'compare')); if ($limit && $count > $limit) { $entries = array_splice($entries, $query->hasOffset() ? $query->getOffset() : 0, $limit); } } return $entries; }