> This function is only available when using OpenLDAP 2.x.x OR Netscape Directory SDK x.x. // Do not check for success. ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); return $ds; } /** * @param $ds * @param array $config * @param array $attr * @return resource */ private function ldapSearch($ds, array $config, array $attr) { // 'Size Limit errors' are reported, even if it is not a real problem. // Fake all errors at the moment. // TODO: just drop the 'Size Limit errors' and report all others set_error_handler(function () { /* ignore errors */ }); $sr = ldap_search($ds, $config[FE_LDAP_BASE_DN], $config[FE_LDAP_SEARCH], $attr, 0, $config[FE_TYPEAHEAD_LIMIT] + 1, $config[FE_LDAP_TIME_LIMIT]); restore_error_handler(); return $sr; } /** * @param array $config * @param string $searchValue * @param string $mode * @return array * @throws UserFormException */ private function prepareConfig(array $config, $searchValue, $mode) { $config[FE_LDAP_ATTRIBUTES] = Support::setIfNotSet($config, FE_LDAP_ATTRIBUTES, ''); $config[FE_LDAP_TIME_LIMIT] = Support::setIfNotSet($config, FE_LDAP_TIME_LIMIT, DEFAULT_LDAP_TIME_LIMIT); $config[FE_LDAP_SEARCH] = str_replace('?', $searchValue, $config[FE_LDAP_SEARCH]); $config[FE_TYPEAHEAD_LIMIT] = ($mode == MODE_LDAP_MULTI) ? $config[FE_TYPEAHEAD_LIMIT] : 1; if ($mode == MODE_LDAP_MULTI) { $config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] = Support::setIfNotSet($config, FE_TYPEAHEAD_LDAP_KEY_PRINTF, ''); $config[FE_TYPEAHEAD_LDAP_VALUE_PRINTF] = Support::setIfNotSet($config, FE_TYPEAHEAD_LDAP_VALUE_PRINTF, ''); if ($config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] == '') { $config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] = $config[FE_TYPEAHEAD_LDAP_VALUE_PRINTF]; } if ($config[FE_TYPEAHEAD_LDAP_VALUE_PRINTF] == '') { $config[FE_TYPEAHEAD_LDAP_VALUE_PRINTF] = $config[FE_TYPEAHEAD_LDAP_KEY_PRINTF]; } if ($mode == MODE_LDAP_MULTI && $config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] == '') { throw new UserFormException("Missing parameter '" . FE_TYPEAHEAD_LDAP_KEY_PRINTF . "' and/or '" . FE_TYPEAHEAD_LDAP_VALUE_PRINTF); } } return $config; } /** * * @param array $config [FE_LDAP_SERVER , FE_LDAP_BASE_DN, FE_LDAP_SEARCH, FE_TYPEAHEAD_LIMIT, FE_TYPEAHEAD_LDAP_KEY_PRINTF, FE_TYPEAHEAD_LDAP_VALUE_PRINTF] * @param string $searchValue value to search via $config[FE_LDAP_SEARCH] * @param string $mode MODE_LDAP_SINGLE | MODE_LDAP_MULTI | MODE_LDAP_PREFETCH * @return array Array: [ [ 'key' => '...', 'value' => '...' ], ] * @throws UserFormException */ public function process(array $config, $searchValue, $mode = MODE_LDAP_MULTI) { $arr = array(); // For TypeAhead, use an optional given F_TYPEAHEAD_LDAP_SEARCH switch ($mode) { case MODE_LDAP_PREFETCH: if (!isset($config[F_TYPEAHEAD_LDAP_SEARCH_PREFETCH]) || $config[F_TYPEAHEAD_LDAP_SEARCH_PREFETCH] == '') { throw new UserFormException("Missing definition for `" . F_TYPEAHEAD_LDAP_SEARCH_PREFETCH . "`", ERROR_MISSING_TYPE_AHEAD_LDAP_SEARCH_PREFETCH); } $config[F_LDAP_SEARCH] = $config[F_TYPEAHEAD_LDAP_SEARCH_PREFETCH]; break; case MODE_LDAP_MULTI: if (!isset($config[F_TYPEAHEAD_LDAP_SEARCH]) || $config[F_TYPEAHEAD_LDAP_SEARCH] == '') { throw new UserFormException("Missing definition for `" . F_TYPEAHEAD_LDAP_SEARCH . "`", ERROR_MISSING_TYPE_AHEAD_LDAP_SEARCH); } $config[F_LDAP_SEARCH] = $config[F_TYPEAHEAD_LDAP_SEARCH]; break; case MODE_LDAP_SINGLE: if (!isset($config[F_LDAP_SEARCH]) || $config[F_LDAP_SEARCH] == '') { throw new UserFormException("Missing definition for `" . F_LDAP_SEARCH . "`", ERROR_MISSING_TYPE_AHEAD_LDAP_SEARCH); } break; default: throw new UserFormException("Unknown mode: " . $mode, ERROR_UNKNOWN_MODE); } $searchValue = Support::ldap_escape($searchValue, null, LDAP_ESCAPE_FILTER); $config = $this->prepareConfig($config, $searchValue, $mode); $ds = $this->ldapConnect($config[FE_LDAP_SERVER]); // must be a valid LDAP server! if (isset($config[SYSTEM_LDAP_1_RDN]) && isset($config[SYSTEM_LDAP_1_PASSWORD])) { if (false === ldap_bind($ds, $config[SYSTEM_LDAP_1_RDN], $config[SYSTEM_LDAP_1_PASSWORD])) { throw new UserFormException("LDAP: Error trying to bind: " . ldap_error($ds), ERROR_LDAP_BIND); } } $keyArr = $this->preparePrintf($config, FE_TYPEAHEAD_LDAP_KEY_PRINTF, $keyFormat); $valueArr = $this->preparePrintf($config, FE_TYPEAHEAD_LDAP_VALUE_PRINTF, $valueFormat); $specificArr = OnArray::arrayValueToLower(OnArray::trimArray(explode(',', $config[FE_LDAP_ATTRIBUTES]))); // merge, trim, toLower, unique, values $attr = array_values( array_unique( OnArray::removeEmptyElementsFromArray( array_merge($keyArr, $valueArr, $specificArr)))); $sr = $this->ldapSearch($ds, $config, $attr); if ($sr !== false) { $info = ldap_get_entries($ds, $sr); if ($mode == MODE_LDAP_MULTI) { // Iterate over all Elements, per element collect all needed attributes for ($i = 0; $i < $info["count"]; $i++) { // HTML Entities will be escaped on Client side. $key = $this->printfResult($keyFormat, $keyArr, $info[$i], false); $value = $this->printfResult($valueFormat, $valueArr, $info[$i], false); if ($key == '' || $value == '') { continue; // if $key or $value is empty: skip } $arr[] = [API_TYPEAHEAD_KEY => $key, API_TYPEAHEAD_VALUE => $value]; } } else { // Collect all attributes foreach ($attr as $key) { $value = isset($info[0][$key][0]) ? $info[0][$key][0] : ''; $arr[$key] = $value; } } ldap_close($ds); } return $arr; } /** * Very specific function to prepare the later 'printfResult()'. * * @param array $config Check existence and take element $key * @param string $key FE_TYPEAHEAD_LDAP_KEY_PRINTF, FE_TYPEAHEAD_LDAP_VALUE_PRINTF * @param string $fmtFirst Returns the first part of $fmtComplete - the printf format string without any args. * @return array Array with all requested keynames from $fmtComplete * @throws CodeException * @throws UserFormException */ private function preparePrintf(array $config, $key, &$fmtFirst) { $fmtFirst = ''; if (!isset($config[$key])) { return array(); } $fmtComplete = $config[$key]; // Typical $fmtComplete: "'%s / %s / %s', cn, mail. telephonenumber" $arr = KeyValueStringParser::explodeWrapped(',', $fmtComplete); if (count($arr) < 2) { throw new UserFormException("Expect a sprintf compatible format string with a least one argument. Got: '" . $fmtComplete . "'", ERROR_MISSING_PRINTF_ARGUMENTS); } // unquote and return the part printf-'formatString' $fmtFirst = trim($arr[0], SINGLE_TICK . DOUBLE_TICK); array_shift($arr); // remove first entry: // toLower & trim are mandatory here: access to LDAP entries are comming soon. return OnArray::arrayValueToLower(OnArray::trimArray($arr)); } /** * Plays sprintf with supplied arguments. Collect the values of the arguments in the array * $keyArr to pass them via 'call_user_func_array' to sprintf. * * @param $format * @param $infoElement * @return string output of sprintf * @throws CodeException * @throws UserFormException */ private function printfResult($format, array $keyArr, $infoElement, $doHtmlEntity = true) { $args = array($format); foreach ($keyArr as $key) { $val = ''; if (isset($infoElement[$key][0])) { $val = $infoElement[$key][0]; if ($doHtmlEntity === true) { $val = htmlentities($val); } } $args[] = $val; } return call_user_func_array('sprintf', $args); } }