Example #1
0
 public function getOption($name)
 {
     if (!@ldap_get_option($this->connection, ConnectionOptions::getOption($name), $ret)) {
         throw new LdapException(sprintf('Could not retrieve value for option "%s".', $name));
     }
     return $ret;
 }
Example #2
0
 /**
  * Gets current value set for an option
  *
  * @param int $option Ldap option name
  *
  * @return mixed value set for the option
  *
  * @throws OptionException if option cannot be retrieved
  */
 public function getOption($option)
 {
     $value = null;
     if (!@ldap_get_option($this->connection, $option, $value)) {
         $code = @ldap_errno($this->connection);
         throw new OptionException(sprintf('Could not retrieve option %s value: Ldap Error Code=%s - %s', $option, $code, ldap_err2str($code)), $code);
     }
     return $value;
 }
Example #3
0
 /**
  * @Route("enrolarverificar/")
  * @Template()
  */
 public function enrolarverificarAction(Request $request)
 {
     $em = $this->getDoctrine()->getManager();
     $Dominio = 'municipiorg.gob.ar';
     $Usuario = $request->get('_username');
     $Contrasena = $request->get('_password');
     $Documento = str_replace(array('.', ' ', '-', ','), '', $request->get('_documento'));
     if (!$Documento || !$Usuario || !$Contrasena) {
         $this->get('session')->getFlashBag()->add('danger', 'Por favor escriba los datos solicitados.');
         return $this->redirect($this->generateUrl('yacare_munirg_ldap_enrolarinicio'));
     }
     $Persona = $em->getRepository('YacareBaseBundle:Persona')->findBy(array('DocumentoNumero' => $Documento));
     if (count($Persona) < 1) {
         $this->get('session')->getFlashBag()->add('danger', 'No se encuentra una persona relacionada al DNI Nº ' . $Documento . ' en la base de datos.');
         return $this->redirect($this->generateUrl('yacare_munirg_ldap_enrolarinicio'));
     } else {
         if (count($Persona) > 1) {
             $this->get('session')->getFlashBag()->add('danger', 'Hay más de una persona asociada al DNI Nº ' . $Documento . ' en la base de datos.');
             return $this->redirect($this->generateUrl('yacare_munirg_ldap_enrolarinicio'));
         }
     }
     $Persona = $Persona[0];
     $IdAgente = $Persona->getAgenteId();
     if (!$IdAgente) {
         $this->get('session')->getFlashBag()->add('danger', 'No se encuentra un agente municipal relacionado al DNI Nº ' . $Documento . '.');
         return $this->redirect($this->generateUrl('yacare_munirg_ldap_enrolarinicio'));
     }
     if ($IdAgente) {
         $Agente = $em->getRepository('YacareRecursosHumanosBundle:Agente')->find($IdAgente);
         if (!$Agente) {
             $this->get('session')->getFlashBag()->add('warning', 'No se encuentra un agente municipal relacionado al DNI Nº ' . $Documento);
             return $this->redirect($this->generateUrl('yacare_munirg_ldap_enrolarinicio'));
         }
     }
     $ServidorAd = \ldap_connect('192.168.100.44');
     ldap_set_option($ServidorAd, LDAP_OPT_PROTOCOL_VERSION, 3);
     ldap_set_option($ServidorAd, LDAP_OPT_REFERRALS, 0);
     $UsrBind = @\ldap_bind($ServidorAd, $Usuario . '@' . $Dominio, $Contrasena);
     if ($UsrBind) {
         return array('agente' => $Agente, 'usuario' => $Usuario, 'contrasena' => $Contrasena, 'documento' => $Documento);
     } else {
         $extended_error = '';
         if (ldap_get_option($ServidorAd, LDAP_OPT_ERROR_STRING, $extended_error)) {
             echo "Error Binding to LDAP: {$extended_error}";
         } else {
             echo "Error Binding to LDAP: No additional information is available.";
         }
         $this->get('session')->getFlashBag()->add('danger', 'No se puede conectar con la cuenta proporcionada. Verifique el nombre de usuario y la contraseña.');
         return $this->redirect($this->generateUrl('yacare_munirg_ldap_enrolarinicio'));
     }
 }
Example #4
0
function authenticate($username, $password)
{
    global $config, $ldap_connection, $auth_error;
    if ($ldap_connection) {
        // bind with sAMAccountName instead of full LDAP DN
        if ($username && $password && ldap_bind($ldap_connection, "{$username}@{$config['auth_ad_domain']}", $password)) {
            // group membership in one of the configured groups is required
            if (isset($config['auth_ad_require_groupmembership']) && $config['auth_ad_require_groupmembership']) {
                $search = ldap_search($ldap_connection, $config['auth_ad_base_dn'], get_auth_ad_user_filter($username), array('memberOf'));
                $entries = ldap_get_entries($ldap_connection, $search);
                unset($entries[0]['memberof']['count']);
                //remove the annoying count
                foreach ($entries[0]['memberof'] as $entry) {
                    $group_cn = get_cn($entry);
                    if (isset($config['auth_ad_groups'][$group_cn]['level'])) {
                        // user is in one of the defined groups
                        adduser($username);
                        return 1;
                    }
                }
                if (isset($config['auth_ad_debug']) && $config['auth_ad_debug']) {
                    if ($entries['count'] == 0) {
                        $auth_error = 'No groups found for user, check base dn';
                    } else {
                        $auth_error = 'User is not in one of the required groups';
                    }
                } else {
                    $auth_error = 'Invalid credentials';
                }
                return 0;
            } else {
                // group membership is not required and user is valid
                adduser($username);
                return 1;
            }
        }
    }
    if (!isset($password) || $password == '') {
        $auth_error = "A password is required";
    } elseif (isset($config['auth_ad_debug']) && $config['auth_ad_debug']) {
        ldap_get_option($ldap_connection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extended_error);
        $auth_error = ldap_error($ldap_connection) . '<br />' . $extended_error;
    } else {
        $auth_error = ldap_error($ldap_connection);
    }
    return 0;
}
 public function authenticate($ps_username, $ps_password = '', $pa_options = null)
 {
     $vo_bind = $this->bindToDirectory($ps_username, $ps_password);
     if (!$vo_bind) {
         if (ldap_get_option($this->getLinkIdentifier(), 0x32, $extended_error)) {
             $vs_bind_rdn = $this->getProcessedConfigValue("ldap_bind_rdn_format", $ps_username, "", "");
             caLogEvent("ERR", "LDAP ERROR (" . ldap_errno($this->getLinkIdentifier()) . ") {$extended_error} [{$vs_bind_rdn}]", "OpenLDAP::Authenticate");
         }
         return false;
     }
     // check group membership
     if (!$this->hasRequiredGroupMembership($ps_username)) {
         return false;
     }
     // user role and group membership syncing with directory
     $this->syncWithDirectory($ps_username);
     return true;
 }
Example #6
0
 function getOption($option)
 {
     $ret = '';
     switch ($option) {
         case 'sid':
             $ret = $this->sid;
             break;
         case 'version':
             $ret = -1;
             ldap_get_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $ret);
             break;
         case 'name':
             $ret = $this->name;
             break;
         case 'port':
             $ret = $this->port;
             break;
         case 'tls':
             $ret = $this->tls;
             break;
         case 'encrypted':
             $ret = $this->encrypted;
             break;
         case 'user_attr':
             $ret = isset($this->user_attr) ? $this->user_attr : NULL;
             break;
         case 'attr_filter':
             $ret = isset($this->attr_filter) ? $this->attr_filter : NULL;
             break;
         case 'basedn':
             $ret = isset($this->basedn) ? $this->basedn : NULL;
             break;
         case 'mail_attr':
             $ret = isset($this->mail_attr) ? $this->mail_attr : NULL;
             break;
         case 'binddn':
             $ret = isset($this->binddn) ? $this->binddn : NULL;
             break;
         case 'bindpw':
             $ret = isset($this->bindpw) ? $this->bindpw : NULL;
             break;
     }
     return $ret;
 }
 public static function getLdapData($userLogin)
 {
     //Соединяемся с каталогом
     global $app;
     $ldapconn = ldap_connect($app->ldap->addr);
     //Выставляем опции
     ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
     ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
     ldap_get_option($ldapconn, LDAP_OPT_ERROR_STRING, $err);
     //авторизуемся в каталоге
     $ldap_bind = ldap_bind($ldapconn, $app->ldap->uname, $app->ldap->pass);
     //ищем в каталоге
     $reslultsrh = ldap_search($ldapconn, 'dc=ab,dc=SRB,dc=local ', "(samaccountname={$userLogin}*)", array("cn", "userprincipalname", "telephonenumber"));
     $get_Res = ldap_get_entries($ldapconn, $reslultsrh);
     $res = array();
     @($res['cn'] = $get_Res['0']['cn']['0']);
     @($res['userprincipalname'] = $get_Res['0']['userprincipalname']['0']);
     @($res['telephonenumber'] = $get_Res['0']['telephonenumber']['0']);
     ldap_unbind($ldapconn);
     return $res;
 }
Example #8
0
 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;
             }
         }
     }
 }
Example #9
0
function user_login_ldap($username, $password)
{
    $ldapsrv = '';
    // set your LDAP servers IP address (ex.192.168.0.1)
    $ldapsrv_domain = '';
    // set your LDAP servers Domain
    $ldaperr['525'] = 'User not found';
    $ldaperr['52e'] = 'Invalid credentials';
    $ldaperr['530'] = 'Not permitted to logon at this time';
    $ldaperr['531'] = 'Not permitted to logon at this workstation';
    $ldaperr['532'] = 'Password expired';
    $ldaperr['533'] = 'Account disabled';
    $ldaperr['701'] = 'Account expired';
    $ldaperr['773'] = 'User must reset password';
    $ldaperr['775'] = 'User account locked';
    if (!($ds = ldap_connect($ldapsrv))) {
        return 'Unable to connect to LDAP server';
    } else {
        ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
        ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
        if ($ger = @ldap_bind($ds, $username . '@' . $ldapsrv_domain, $password)) {
            return true;
        } else {
            ldap_get_option($ds, LDAP_OPT_ERROR_STRING, $diagmsg);
            if (isset($diagmsg)) {
                $diagmsg2 = explode(',', $diagmsg);
            }
            if (isset($diagmsg2) and preg_match('/data (.*)/i', trim($diagmsg2[2]), $res2) and isset($ldaperr[$res2[1]])) {
                return $ldaperr[$res2[1]];
            } else {
                return ldap_error($ds);
            }
        }
        ldap_close($ds);
    }
}
Example #10
0
 function ServerInfo()
 {
     if (is_array($this->version)) {
         return $this->version;
     }
     $version = array();
     /*
     Determines how aliases are handled during search. 
     LDAP_DEREF_NEVER (0x00)
     LDAP_DEREF_SEARCHING (0x01)
     LDAP_DEREF_FINDING (0x02)
     LDAP_DEREF_ALWAYS (0x03)
     The LDAP_DEREF_SEARCHING value means aliases are dereferenced during the search but 
     not when locating the base object of the search. The LDAP_DEREF_FINDING value means 
     aliases are dereferenced when locating the base object but not during the search.  
     Default: LDAP_DEREF_NEVER
     */
     ldap_get_option($this->_connectionID, LDAP_OPT_DEREF, $version['LDAP_OPT_DEREF']);
     switch ($version['LDAP_OPT_DEREF']) {
         case 0:
             $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_NEVER';
         case 1:
             $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_SEARCHING';
         case 2:
             $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_FINDING';
         case 3:
             $version['LDAP_OPT_DEREF'] = 'LDAP_DEREF_ALWAYS';
     }
     /* 
     A limit on the number of entries to return from a search. 
     LDAP_NO_LIMIT (0) means no limit.
     Default: LDAP_NO_LIMIT
     */
     ldap_get_option($this->_connectionID, LDAP_OPT_SIZELIMIT, $version['LDAP_OPT_SIZELIMIT']);
     if ($version['LDAP_OPT_SIZELIMIT'] == 0) {
         $version['LDAP_OPT_SIZELIMIT'] = 'LDAP_NO_LIMIT';
     }
     /*
     A limit on the number of seconds to spend on a search. 
     LDAP_NO_LIMIT (0) means no limit.
     Default: LDAP_NO_LIMIT
     */
     ldap_get_option($this->_connectionID, LDAP_OPT_TIMELIMIT, $version['LDAP_OPT_TIMELIMIT']);
     if ($version['LDAP_OPT_TIMELIMIT'] == 0) {
         $version['LDAP_OPT_TIMELIMIT'] = 'LDAP_NO_LIMIT';
     }
     /*
     Determines whether the LDAP library automatically follows referrals returned by LDAP servers or not. 
     LDAP_OPT_ON
     LDAP_OPT_OFF
     Default: ON
     */
     ldap_get_option($this->_connectionID, LDAP_OPT_REFERRALS, $version['LDAP_OPT_REFERRALS']);
     if ($version['LDAP_OPT_REFERRALS'] == 0) {
         $version['LDAP_OPT_REFERRALS'] = 'LDAP_OPT_OFF';
     } else {
         $version['LDAP_OPT_REFERRALS'] = 'LDAP_OPT_ON';
     }
     /*
     Determines whether LDAP I/O operations are automatically restarted if they abort prematurely. 
     LDAP_OPT_ON
     LDAP_OPT_OFF
     Default: OFF
     */
     ldap_get_option($this->_connectionID, LDAP_OPT_RESTART, $version['LDAP_OPT_RESTART']);
     if ($version['LDAP_OPT_RESTART'] == 0) {
         $version['LDAP_OPT_RESTART'] = 'LDAP_OPT_OFF';
     } else {
         $version['LDAP_OPT_RESTART'] = 'LDAP_OPT_ON';
     }
     /*
     This option indicates the version of the LDAP protocol used when communicating with the primary LDAP server.
     LDAP_VERSION2 (2)
     LDAP_VERSION3 (3)
     Default: LDAP_VERSION2 (2)
     */
     ldap_get_option($this->_connectionID, LDAP_OPT_PROTOCOL_VERSION, $version['LDAP_OPT_PROTOCOL_VERSION']);
     if ($version['LDAP_OPT_PROTOCOL_VERSION'] == 2) {
         $version['LDAP_OPT_PROTOCOL_VERSION'] = 'LDAP_VERSION2';
     } else {
         $version['LDAP_OPT_PROTOCOL_VERSION'] = 'LDAP_VERSION3';
     }
     /* The host name (or list of hosts) for the primary LDAP server. */
     ldap_get_option($this->_connectionID, LDAP_OPT_HOST_NAME, $version['LDAP_OPT_HOST_NAME']);
     ldap_get_option($this->_connectionID, OPT_ERROR_NUMBER, $version['OPT_ERROR_NUMBER']);
     ldap_get_option($this->_connectionID, OPT_ERROR_STRING, $version['OPT_ERROR_STRING']);
     ldap_get_option($this->_connectionID, LDAP_OPT_MATCHED_DN, $version['LDAP_OPT_MATCHED_DN']);
     return $this->version = $version;
 }
 /**
  * Find a user record using the username and password provided.
  *
  * @param string $username The username/identifier.
  * @param string|null $password The password
  * @return bool|array Either false on failure, or an array of user data.
  */
 protected function _findUser($username, $password = null)
 {
     if (!empty($this->_config['domain']) && !empty($username) && strpos($username, '@') === false) {
         $username .= '@' . $this->_config['domain'];
     }
     set_error_handler(function ($errorNumber, $errorText, $errorFile, $errorLine) {
         throw new ErrorException($errorText, 0, $errorNumber, $errorFile, $errorLine);
     }, E_ALL);
     try {
         $ldapBind = ldap_bind($this->ldapConnection, isset($this->_config['bindDN']) ? $this->_config['bindDN']($username, $this->_config['domain']) : $username, $password);
         if ($ldapBind === true) {
             $searchResults = ldap_search($this->ldapConnection, $this->_config['baseDN']($username, $this->_config['domain']), '(' . $this->_config['search'] . '=' . $username . ')');
             $entry = ldap_first_entry($this->ldapConnection, $searchResults);
             return ldap_get_attributes($this->ldapConnection, $entry);
         }
     } catch (ErrorException $e) {
         if ($this->logErrors === true) {
             $this->log($e->getMessage());
         }
         if (ldap_get_option($this->ldapConnection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extendedError)) {
             if (!empty($extendedError)) {
                 foreach ($this->_config['errors'] as $error => $errorMessage) {
                     if (strpos($extendedError, $error) !== false) {
                         $messages[] = ['message' => $errorMessage, 'key' => $this->_config['flash']['key'], 'element' => $this->_config['flash']['element'], 'params' => $this->_config['flash']['params']];
                     }
                 }
             }
         }
     }
     restore_error_handler();
     if (!empty($messages)) {
         $controller = $this->_registry->getController();
         $controller->request->session()->write('Flash.' . $this->_config['flash']['key'], $messages);
     }
     return false;
 }
Example #12
0
 /**
  * Fetch data from LDAP server
  *
  * Searches the LDAP server for the given username/password
  * combination.  Escapes all LDAP meta characters in username
  * before performing the query.
  *
  * @param  string Username
  * @param  string Password
  * @return boolean
  */
 function fetchData($username, $password)
 {
     $this->log('Auth_Container_LDAP::fetchData() called.', AUTH_LOG_DEBUG);
     $err = $this->_prepare();
     if ($err !== true) {
         return PEAR::raiseError($err->getMessage(), $err->getCode());
     }
     $err = $this->_getBaseDN();
     if ($err !== true) {
         return PEAR::raiseError($err->getMessage(), $err->getCode());
     }
     // UTF8 Encode username for LDAPv3
     if (@ldap_get_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $ver) && $ver == 3) {
         $this->log('UTF8 encoding username for LDAPv3', AUTH_LOG_DEBUG);
         $username = utf8_encode($username);
     }
     // make search filter
     $filter = sprintf('(&(%s=%s)%s)', $this->options['userattr'], $this->_quoteFilterString($username), $this->options['userfilter']);
     // make search base dn
     $search_basedn = $this->options['userdn'];
     if ($search_basedn != '' && substr($search_basedn, -1) != ',') {
         $search_basedn .= ',';
     }
     $search_basedn .= $this->options['basedn'];
     // attributes
     $searchAttributes = $this->options['attributes'];
     // make functions params array
     $func_params = array($this->conn_id, $search_basedn, $filter, $searchAttributes);
     // search function to use
     $func_name = $this->_scope2function($this->options['userscope']);
     $this->log("Searching with {$func_name} and filter {$filter} in {$search_basedn}", AUTH_LOG_DEBUG);
     // search
     if (($result_id = @call_user_func_array($func_name, $func_params)) === false) {
         $this->log('User not found', AUTH_LOG_DEBUG);
     } elseif (@ldap_count_entries($this->conn_id, $result_id) >= 1) {
         // did we get some possible results?
         $this->log('User(s) found', AUTH_LOG_DEBUG);
         $first = true;
         $entry_id = null;
         do {
             // then get the user dn
             if ($first) {
                 $entry_id = @ldap_first_entry($this->conn_id, $result_id);
                 $first = false;
             } else {
                 $entry_id = @ldap_next_entry($this->conn_id, $entry_id);
                 if ($entry_id === false) {
                     break;
                 }
             }
             $user_dn = @ldap_get_dn($this->conn_id, $entry_id);
             // as the dn is not fetched as an attribute, we save it anyway
             if (is_array($searchAttributes) && in_array('dn', $searchAttributes)) {
                 $this->log('Saving DN to AuthData', AUTH_LOG_DEBUG);
                 $this->_auth_obj->setAuthData('dn', $user_dn);
             }
             // fetch attributes
             if ($attributes = @ldap_get_attributes($this->conn_id, $entry_id)) {
                 if (is_array($attributes) && isset($attributes['count']) && $attributes['count'] > 0) {
                     // ldap_get_attributes() returns a specific multi dimensional array
                     // format containing all the attributes and where each array starts
                     // with a 'count' element providing the number of attributes in the
                     // entry, or the number of values for attribute. For compatibility
                     // reasons, it remains the default format returned by LDAP container
                     // setAuthData().
                     // The code below optionally returns attributes in another format,
                     // more compliant with other Auth containers, where each attribute
                     // element are directly set in the 'authData' list. This option is
                     // enabled by setting 'attrformat' to
                     // 'AUTH' in the 'options' array.
                     // eg. $this->options['attrformat'] = 'AUTH'
                     if (strtoupper($this->options['attrformat']) == 'AUTH') {
                         $this->log('Saving attributes to Auth data in AUTH format', AUTH_LOG_DEBUG);
                         unset($attributes['count']);
                         foreach ($attributes as $attributeName => $attributeValue) {
                             if (is_int($attributeName)) {
                                 continue;
                             }
                             if (is_array($attributeValue) && isset($attributeValue['count'])) {
                                 unset($attributeValue['count']);
                             }
                             if (count($attributeValue) <= 1) {
                                 $attributeValue = $attributeValue[0];
                             }
                             $this->log('Storing additional field: ' . $attributeName, AUTH_LOG_DEBUG);
                             $this->_auth_obj->setAuthData($attributeName, $attributeValue);
                         }
                     } else {
                         $this->log('Saving attributes to Auth data in LDAP format', AUTH_LOG_DEBUG);
                         $this->_auth_obj->setAuthData('attributes', $attributes);
                     }
                 }
             }
             @ldap_free_result($result_id);
             // need to catch an empty password as openldap seems to return TRUE
             // if anonymous binding is allowed
             if ($password != "") {
                 $this->log("Bind as {$user_dn}", AUTH_LOG_DEBUG);
                 // try binding as this user with the supplied password
                 if (@ldap_bind($this->conn_id, $user_dn, $password)) {
                     $this->log('Bind successful', AUTH_LOG_DEBUG);
                     // check group if appropiate
                     if (strlen($this->options['group'])) {
                         // decide whether memberattr value is a dn or the username
                         $this->log('Checking group membership', AUTH_LOG_DEBUG);
                         $return = $this->checkGroup($this->options['memberisdn'] ? $user_dn : $username);
                         $this->_disconnect();
                         return $return;
                     } else {
                         $this->log('Authenticated', AUTH_LOG_DEBUG);
                         $this->_disconnect();
                         return true;
                         // user authenticated
                     }
                     // checkGroup
                 }
                 // bind
             }
             // non-empty password
         } while ($this->options['try_all'] == true);
         // interate through entries
     }
     // get results
     // default
     $this->log('NOT authenticated!', AUTH_LOG_DEBUG);
     $this->_disconnect();
     return false;
 }
Example #13
0
 public function testExplicitNetworkTimeoutConnect()
 {
     $networkTimeout = 1;
     $host = TESTS_ZEND_LDAP_HOST;
     $port = 0;
     if (defined('TESTS_ZEND_LDAP_PORT') && TESTS_ZEND_LDAP_PORT != 389) {
         $port = TESTS_ZEND_LDAP_PORT;
     }
     $useSsl = false;
     if (defined('TESTS_ZEND_LDAP_USE_SSL')) {
         $useSsl = TESTS_ZEND_LDAP_USE_SSL;
     }
     $ldap = new Ldap\Ldap();
     $ldap->connect($host, $port, $useSsl, null, $networkTimeout);
     ldap_get_option($ldap->getResource(), LDAP_OPT_NETWORK_TIMEOUT, $actual);
     $this->assertEquals($networkTimeout, $actual);
 }
Example #14
0
 /**
  * Returns true if the username and password work and false if they are
  * wrong or don't exist.
  *
  * @param string $username The username (without system magic quotes)
  * @param string $password The password (without system magic quotes)
  *
  * @return bool Authentication success or failure.
  */
 function user_login($username, $password)
 {
     if (!function_exists('ldap_bind')) {
         print_error('auth_ldapnotinstalled', 'auth_ldap');
         return false;
     }
     if (!$username or !$password) {
         // Don't allow blank usernames or passwords
         return false;
     }
     $extusername = core_text::convert($username, 'utf-8', $this->config->ldapencoding);
     $extpassword = core_text::convert($password, 'utf-8', $this->config->ldapencoding);
     // Before we connect to LDAP, check if this is an AD SSO login
     // if we succeed in this block, we'll return success early.
     //
     $key = sesskey();
     if (!empty($this->config->ntlmsso_enabled) && $key === $password) {
         $cf = get_cache_flags($this->pluginconfig . '/ntlmsess');
         // We only get the cache flag if we retrieve it before
         // it expires (AUTH_NTLMTIMEOUT seconds).
         if (!isset($cf[$key]) || $cf[$key] === '') {
             return false;
         }
         $sessusername = $cf[$key];
         if ($username === $sessusername) {
             unset($sessusername);
             unset($cf);
             // Check that the user is inside one of the configured LDAP contexts
             $validuser = false;
             $ldapconnection = $this->ldap_connect();
             // if the user is not inside the configured contexts,
             // ldap_find_userdn returns false.
             if ($this->ldap_find_userdn($ldapconnection, $extusername)) {
                 $validuser = true;
             }
             $this->ldap_close();
             // Shortcut here - SSO confirmed
             return $validuser;
         }
     }
     // End SSO processing
     unset($key);
     $ldapconnection = $this->ldap_connect();
     $ldap_user_dn = $this->ldap_find_userdn($ldapconnection, $extusername);
     // If ldap_user_dn is empty, user does not exist
     if (!$ldap_user_dn) {
         $this->ldap_close();
         return false;
     }
     // Try to bind with current username and password
     $ldap_login = @ldap_bind($ldapconnection, $ldap_user_dn, $extpassword);
     // If login fails and we are using MS Active Directory, retrieve the diagnostic
     // message to see if this is due to an expired password, or that the user is forced to
     // change the password on first login. If it is, only proceed if we can change
     // password from Moodle (otherwise we'll get stuck later in the login process).
     if (!$ldap_login && $this->config->user_type == 'ad' && $this->can_change_password() && (!empty($this->config->expiration) and $this->config->expiration == 1)) {
         // We need to get the diagnostic message right after the call to ldap_bind(),
         // before any other LDAP operation.
         ldap_get_option($ldapconnection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $diagmsg);
         if ($this->ldap_ad_pwdexpired_from_diagmsg($diagmsg)) {
             // If login failed because user must change the password now or the
             // password has expired, let the user in. We'll catch this later in the
             // login process when we explicitly check for expired passwords.
             $ldap_login = true;
         }
     }
     $this->ldap_close();
     return $ldap_login;
 }
Example #15
0
function ConnectToAD($username, $password)
{
    $ChilliConf = unserialize(base64_decode(@file_get_contents("/etc/artica-postfix/settings/Daemons/ChilliConf")));
    if ($ChilliConf["EnableActiveDirectory"] == 0) {
        return false;
    }
    $AD_DOMAIN = $ChilliConf["AD_DOMAIN"];
    define(LDAP_OPT_DIAGNOSTIC_MESSAGE, 0x32);
    events("ldap_connect({$ChilliConf["AD_SERVER"]},{$ChilliConf["AD_PORT"]})...");
    $cnx = @ldap_connect($ChilliConf["AD_SERVER"], $ChilliConf["AD_PORT"]);
    if (!$cnx) {
        events("Fatal: ldap_connect({$ChilliConf["AD_SERVER"]},{$ChilliConf["AD_PORT"]} ) Check your configuration...");
        @ldap_close();
        return false;
    }
    events("OK: ldap_connect({$ChilliConf["AD_SERVER"]},{$ChilliConf["AD_PORT"]} ) SUCCESS");
    @ldap_set_option($cnx, LDAP_OPT_PROTOCOL_VERSION, 3);
    @ldap_set_option($cnx, LDAP_OPT_REFERRALS, 0);
    @ldap_set_option($cnx, LDAP_OPT_PROTOCOL_VERSION, 3);
    // on passe le LDAP en version 3, necessaire pour travailler avec le AD
    @ldap_set_option($cnx, LDAP_OPT_REFERRALS, 0);
    events("Check ident {$username}@{$AD_DOMAIN} {$password}");
    $bind = @ldap_bind($cnx, "{$username}@{$AD_DOMAIN}", $password);
    if (!$bind) {
        $errn = ldap_errno($cnx);
        $error = "Error {$errn}: " . ldap_err2str($errn);
        if (@ldap_get_option($cnx, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extended_error)) {
            $error = $error . " {$extended_error}";
        }
        events("{$error}");
        return false;
    }
    events("Active Directory session  SUCCESS");
    return true;
}
Example #16
0
 /**
  * Connect to the LDAP server using the global options
  *
  * @access private
  * @return object  Returns a PEAR error object if an error occurs.
  */
 function _connect()
 {
     // connect
     if (isset($this->options['url']) && $this->options['url'] != '') {
         $this->conn_id = @ldap_connect($this->options['url']);
     } else {
         $this->conn_id = @ldap_connect($this->options['host'], $this->options['port']);
     }
     // try switchig to LDAPv3
     $ver = 0;
     if (@ldap_get_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $ver) && $ver >= 2) {
         @ldap_set_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, 3);
     }
     // bind anonymously for searching
     if (@ldap_bind($this->conn_id) == false) {
         return PEAR::raiseError("Auth_Container_LDAP: Could not connect and bind to LDAP server.", 41, PEAR_ERROR_DIE);
     }
 }
Example #17
0
 /**
  * Get an LDAP option value
  *
  * @param string $option Option to get
  *
  * @access public
  * @return Net_LDAP2_Error|string Net_LDAP2_Error or option value
  */
 public function getOption($option)
 {
     if ($this->_link) {
         if (defined($option)) {
             if (@ldap_get_option($this->_link, constant($option), $value)) {
                 return $value;
             } else {
                 $err = @ldap_errno($this->_link);
                 if ($err) {
                     $msg = @ldap_err2str($err);
                 } else {
                     $err = NET_LDAP2_ERROR;
                     $msg = Net_LDAP2::errorMessage($err);
                 }
                 return $this->raiseError($msg, $err);
             }
         } else {
             $this->raiseError("Unkown Option requested");
         }
     } else {
         $this->raiseError("No LDAP connection");
     }
 }
Example #18
0
 /**
  * Convenience method to create an LDAPException as well as log the
  * description.
  *
  * @param string $description
  * The exception's description
  * @return Exception
  */
 private function makeException($description, $type = NULL)
 {
     $errNo = 0x0;
     // Log LDAP code and description, if possible.
     if (empty($this->ldap)) {
         SimpleSAML_Logger::error($description);
     } else {
         $errNo = @ldap_errno($this->ldap);
     }
     // Decide exception type and return
     if ($type) {
         if ($errNo !== 0) {
             // Only log real LDAP errors; not success.
             SimpleSAML_Logger::error($description . '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')');
         } else {
             SimpleSAML_Logger::error($description);
         }
         switch ($type) {
             case ERR_INTERNAL:
                 // 1 - ExInternal
                 return new SimpleSAML_Error_Exception($description, $errNo);
             case ERR_NO_USER:
                 // 2 - ExUserNotFound
                 return new SimpleSAML_Error_UserNotFound($description, $errNo);
             case ERR_WRONG_PW:
                 // 3 - ExInvalidCredential
                 return new SimpleSAML_Error_InvalidCredential($description, $errNo);
             case ERR_AS_DATA_INCONSIST:
                 // 4 - ExAsDataInconsist
                 return new SimpleSAML_Error_AuthSource('ldap', $description);
             case ERR_AS_INTERNAL:
                 // 5 - ExAsInternal
                 return new SimpleSAML_Error_AuthSource('ldap', $description);
         }
     } else {
         if ($errNo !== 0) {
             $description .= '; cause: \'' . ldap_error($this->ldap) . '\' (0x' . dechex($errNo) . ')';
             if (@ldap_get_option($this->ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extendedError) && !empty($extendedError)) {
                 $description .= '; additional: \'' . $extendedError . '\'';
             }
         }
         switch ($errNo) {
             case 0x20:
                 //LDAP_NO_SUCH_OBJECT
                 SimpleSAML_Logger::warning($description);
                 return new SimpleSAML_Error_UserNotFound($description, $errNo);
             case 0x31:
                 //LDAP_INVALID_CREDENTIALS
                 SimpleSAML_Logger::info($description);
                 return new SimpleSAML_Error_InvalidCredential($description, $errNo);
             case -1:
                 //NO_SERVER_CONNECTION
                 SimpleSAML_Logger::error($description);
                 return new SimpleSAML_Error_AuthSource('ldap', $description);
             default:
                 SimpleSAML_Logger::error($description);
                 return new SimpleSAML_Error_AuthSource('ldap', $description);
         }
     }
 }
Example #19
0
 /**
  * Returns an LDAP option value.
  *
  * @param string $option Option to get.
  *
  * @return Horde_Ldap_Error|string Horde_Ldap_Error or option value
  * @throws Horde_Ldap_Exception
  */
 public function getOption($option)
 {
     if (!$this->_link) {
         throw new Horde_Ldap_Exception('No LDAP connection');
     }
     if (!defined($option)) {
         throw new Horde_Ldap_Exception('Unkown option requested');
     }
     if (@ldap_get_option($this->_link, constant($option), $value)) {
         return $value;
     }
     $err = @ldap_errno($this->_link);
     if ($err) {
         throw new Horde_Ldap_Exception(ldap_err2str($err), $err);
     }
     throw new Horde_Ldap_Exception('Unknown error');
 }
Example #20
0
 /**
  * Retrieve ldap option value
  *
  * @param   int option
  * @return  var value
  */
 public function getOption($option)
 {
     if (false === ($res = ldap_get_option($this->_hdl, $option, $value))) {
         throw new LDAPException('Cannot get value "' . $option . '"', ldap_errno($this->_hdl));
     }
     return $value;
 }
Example #21
0
 /**
  * Verification de la version du serveur ldap.
  *
  * @return	string					version
  */
 function getVersion()
 {
     $version = 0;
     $version = @ldap_get_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, $version);
     return $version;
 }
Example #22
0
<?php

require "connect.inc";
$link = ldap_connect($host, $port);
$option = null;
var_dump(ldap_set_option($link, LDAP_OPT_PROTOCOL_VERSION, $protocol_version));
ldap_get_option($link, LDAP_OPT_PROTOCOL_VERSION, $option);
var_dump($option);
?>
===DONE===
Example #23
0
 /**
  * Return the diagnostic Message.
  *
  * @return string $diagnosticMessage
  */
 public function getDiagnosticMessage()
 {
     ldap_get_option($this->getConnection(), LDAP_OPT_ERROR_STRING, $diagnosticMessage);
     return $diagnosticMessage;
 }
function ConnectToLDAP()
{
    BuildDefault();
    $array = $GLOBALS["KerbAuthInfos"];
    if (!is_array($array)) {
        WLOG("KerbAuthInfos not an array");
        return false;
    }
    if (!isset($array["LDAP_SERVER"])) {
        WLOG("LDAP_SERVER not set");
        return;
    }
    if (!isset($array["LDAP_SUFFIX"])) {
        WLOG("LDAP_SUFFIX not set");
        return;
    }
    $GLOBALS["SUFFIX"] = $array["LDAP_SUFFIX"];
    $GLOBALS["CONNECTION"] = @ldap_connect($array["LDAP_SERVER"], $array["LDAP_PORT"]);
    //WLOG("[LDAP]: Connecting to LDAP server `{$array["LDAP_SERVER"]}:{$array["LDAP_PORT"]}`");
    if (!$GLOBALS["CONNECTION"]) {
        WLOG("[LDAP]: Fatal: ldap_connect({$array["LDAP_SERVER"]},{$array["LDAP_PORT"]} )");
        @ldap_close();
        return false;
    }
    //WLOG("[LDAP]: Connecting to LDAP server {$array["LDAP_SERVER"]} <span style='font-weight:bold;color:#00B218'>success</span> with suffix:&laquo;{$GLOBALS["SUFFIX"]}&raquo;");
    @ldap_set_option($GLOBALS["CONNECTION"], LDAP_OPT_PROTOCOL_VERSION, 3);
    @ldap_set_option($GLOBALS["CONNECTION"], LDAP_OPT_REFERRALS, 0);
    @ldap_set_option($GLOBALS["CONNECTION"], LDAP_OPT_PROTOCOL_VERSION, 3);
    // on passe le LDAP en version 3, necessaire pour travailler avec le AD
    @ldap_set_option($GLOBALS["CONNECTION"], LDAP_OPT_REFERRALS, 0);
    if (preg_match("#^(.+?)\\/(.+?)\$#", $array["WINDOWS_SERVER_ADMIN"], $re)) {
        $array["WINDOWS_SERVER_ADMIN"] = $re[1];
    }
    if (preg_match("#^(.+?)\\\\(.+?)\$#", $array["WINDOWS_SERVER_ADMIN"], $re)) {
        $array["WINDOWS_SERVER_ADMIN"] = $re[1];
    }
    //$GLOBALS["BIND"]=ldap_bind($GLOBALS["CONNECTION"], $array["LDAP_DN"], $array["LDAP_PASSWORD"]);
    $GLOBALS["BIND"] = @ldap_bind($GLOBALS["CONNECTION"], "{$array["WINDOWS_SERVER_ADMIN"]}@{$array["WINDOWS_DNS_SUFFIX"]}", $array["WINDOWS_SERVER_PASS"]);
    if (!$GLOBALS["BIND"]) {
        if (@ldap_get_option($GLOBALS["CONNECTION"], LDAP_OPT_DIAGNOSTIC_MESSAGE, $extended_error)) {
            $error = $error . " {$extended_error}";
        }
        switch (ldap_errno($GLOBALS["CONNECTION"])) {
            case 0x31:
                $error = $error . " Bad username or password. Please try again.";
                break;
            case 0x32:
                $error = $error . " Insufficient access rights.";
                break;
            case 81:
                $error = $error . " Unable to connect to the LDAP server \n\t\t\t\t{$array["LDAP_SERVER"]} please, verify if ldap daemon is running  or the ldap server address";
                break;
            case -1:
                break;
            default:
                $error = $error . " Could not bind to the LDAP server." . " " . @ldap_err2str($GLOBALS["CONNECTION"]);
        }
        WLOG("[LDAP]:" . __LINE__ . " Connecting to LDAP server {$array["LDAP_SERVER"]} failed {$error}");
        return false;
    }
    //WLOG("[LDAP]: Binding to LDAP server {$array["LDAP_SERVER"]} <span style='font-weight:bold;color:#00B218'>success</span>.");
    return true;
}
Example #25
0
 /**
  * Connect Method
  */
 function connect()
 {
     if (!($con = ldap_connect($this->address, $this->port))) {
         watchdog('user', 'LDAP Connect failure to ' . $this->address . ':' . $this->port);
         return LDAP_CONNECT_ERROR;
     }
     ldap_set_option($con, LDAP_OPT_PROTOCOL_VERSION, 3);
     ldap_set_option($con, LDAP_OPT_REFERRALS, (int) $this->followrefs);
     // Use TLS if we are configured and able to.
     if ($this->tls) {
         ldap_get_option($con, LDAP_OPT_PROTOCOL_VERSION, $vers);
         if ($vers == -1) {
             watchdog('user', 'Could not get LDAP protocol version.');
             return LDAP_PROTOCOL_ERROR;
         }
         if ($vers != 3) {
             watchdog('user', 'Could not start TLS, only supported by LDAP v3.');
             return LDAP_CONNECT_ERROR;
         } elseif (!function_exists('ldap_start_tls')) {
             watchdog('user', 'Could not start TLS. It does not seem to be supported by this PHP setup.');
             return LDAP_CONNECT_ERROR;
         } elseif (!ldap_start_tls($con)) {
             $msg = t("Could not start TLS. (Error %errno: %error).", array('%errno' => ldap_errno($con), '%error' => ldap_error($con)));
             watchdog('user', $msg);
             return LDAP_CONNECT_ERROR;
         }
     }
     // Store the resulting resource
     $this->connection = $con;
     return LDAP_SUCCESS;
 }
function TestLDAPAD_bind($server, $port, $user, $password)
{
    $ldap_connection = @ldap_connect($server, $port);
    if (!$ldap_connection) {
        $error = "[CONNECT] ldap://{$server}:{$port}";
        if (@ldap_get_option($ldap_connection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extended_error)) {
            $error = $error . "<br>{$extended_error}";
            if ($GLOBALS["VERBOSE"]) {
                echo "<strong style='color:red'>{$error}</strong><br>\n";
            }
        }
        @ldap_close($ldap_connection);
        return $error;
    }
    @ldap_set_option($ldap_connection, LDAP_OPT_REFERRALS, 0);
    @ldap_set_option($ldap_connection, LDAP_OPT_REFERRALS, 0);
    @ldap_set_option($ldap_connection, LDAP_OPT_PROTOCOL_VERSION, 3);
    @ldap_set_option($ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, 1);
    $bind = ldap_bind($ldap_connection, "{$user}", $password);
    if (!$bind) {
        $error = "[BIND] ldap://{$server}:{$port}";
        if (@ldap_get_option($ldap_connection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extended_error)) {
            $error = $error . "<br>{$extended_error}";
            if ($GLOBALS["VERBOSE"]) {
                echo "<strong style='color:red'>{$error}</strong><br>\n";
            }
        }
        @ldap_close($ldap_connection);
        return $error;
    }
    if ($GLOBALS["VERBOSE"]) {
        echo "<strong style='color:red'>ldap://{$server}:{$port} [OK]</strong><br>\n";
    }
    return null;
}
Example #27
0
function ldap_check_credentials($user, $pass)
{
    foreach (explode(' ', LDAP_SERVERS) as $server) {
        // The connection may only be really established when needed,
        // so execute a dummy query to test if the server is available:
        $conn = @ldap_connect($server);
        if (!$conn || !ldap_get_option($conn, LDAP_OPT_PROTOCOL_VERSION, $dummy)) {
            continue;
        }
        /*
        		// The following options are necessary to be able to talk
                // to an Active Directory:
        		if ( !ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3) ) {
        			error("Failed to set protocol version to 3.");
        		}
        
        		if ( !ldap_set_option($conn, LDAP_OPT_REFERRALS, 0) ) {
        			error("Failed to set LDAP_OPT_REFERRALS.");
        		}
        
        		if ( !ldap_set_option($conn, LDAP_OPT_DEREF, 0) ) {
        			error("Failed to set LDAP_OPT_DEREF.");
        		}
        */
        // Create the dn
        $ldap_dn = str_replace('&', $user, LDAP_DNQUERY);
        // Try to login to test credentials
        if (@ldap_bind($conn, $ldap_dn, $pass)) {
            @ldap_unbind($conn);
            return TRUE;
        }
    }
    return FALSE;
}
 /**
  * Connect to the LDAP server using the global options
  *
  * @access private
  * @return object  Returns a PEAR error object if an error occurs.
  */
 function _connect()
 {
     // connect
     if (isset($this->options['url']) && $this->options['url'] != '') {
         $this->_debug('Connecting with URL', __LINE__);
         $conn_params = array($this->options['url']);
     } else {
         $this->_debug('Connecting with host:port', __LINE__);
         $conn_params = array($this->options['host'], $this->options['port']);
     }
     if (($this->conn_id = @call_user_func_array('ldap_connect', $conn_params)) === false) {
         return PEAR::raiseError('Auth_Container_LDAP: Could not connect to server.', 41, PEAR_ERROR_DIE);
     }
     $this->_debug('Successfully connected to server', __LINE__);
     // try switchig to LDAPv3
     $ver = 0;
     if (@ldap_get_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $ver) && $ver >= 2) {
         $this->_debug('Switching to LDAPv3', __LINE__);
         @ldap_set_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, 3);
     }
     // bind with credentials or anonymously
     if ($this->options['binddn'] && $this->options['bindpw']) {
         $this->_debug('Binding with credentials', __LINE__);
         $bind_params = array($this->conn_id, $this->options['binddn'], $this->options['bindpw']);
     } else {
         $this->_debug('Binding anonymously', __LINE__);
         $bind_params = array($this->conn_id);
     }
     // bind for searching
     if (@call_user_func_array('ldap_bind', $bind_params) == false) {
         $this->_debug();
         $this->_disconnect();
         return PEAR::raiseError("Auth_Container_LDAP: Could not bind to LDAP server.", 41, PEAR_ERROR_DIE);
     }
     $this->_debug('Binding was successful', __LINE__);
 }
Example #29
0
 /**
  * Return the LDAP error message of the last LDAP command
  *
  * @param  int   $errorCode
  * @param  array $errorMessages
  * @return string
  */
 public function getLastError(&$errorCode = null, array &$errorMessages = null)
 {
     $errorCode = $this->getLastErrorCode();
     $errorMessages = array();
     /* The various error retrieval functions can return
      * different things so we just try to collect what we
      * can and eliminate dupes.
      */
     $estr1 = @ldap_error($this->_resource);
     if ($errorCode !== 0 && $estr1 === 'Success') {
         $estr1 = @ldap_err2str($errorCode);
     }
     if (!empty($estr1)) {
         $errorMessages[] = $estr1;
     }
     @ldap_get_option($this->_resource, LDAP_OPT_ERROR_STRING, $estr2);
     if (!empty($estr2) && !in_array($estr2, $errorMessages)) {
         $errorMessages[] = $estr2;
     }
     $message = '';
     if ($errorCode > 0) {
         $message = '0x' . dechex($errorCode) . ' ';
     } else {
         $message = '';
     }
     if (count($errorMessages) > 0) {
         $message .= '(' . implode('; ', $errorMessages) . ')';
     } else {
         $message .= '(no error message from LDAP)';
     }
     return $message;
 }
Example #30
0
 /**
  * Evaluates the given variable
  *
  * @param   mixed &$subject   Variable to query
  * @param   bool $specialStr  Should this be interpreted as a special string?
  * @return  mixed             Result (both HTML and text modes generate strings)
  */
 protected function evaluate(&$subject, $specialStr = false)
 {
     switch ($type = gettype($subject)) {
         // https://github.com/digitalnature/php-ref/issues/13
         case 'unknown type':
             return $this->fmt->text('unknown');
             // null value
         // null value
         case 'NULL':
             return $this->fmt->text('null');
             // integer/double/float
         // integer/double/float
         case 'integer':
         case 'double':
             return $this->fmt->text($type, $subject, $type);
             // boolean
         // boolean
         case 'boolean':
             $text = $subject ? 'true' : 'false';
             return $this->fmt->text($text, $text, $type);
             // arrays
         // arrays
         case 'array':
             // empty array?
             if (empty($subject)) {
                 $this->fmt->text('array');
                 return $this->fmt->emptyGroup();
             }
             if (isset($subject[static::MARKER_KEY])) {
                 unset($subject[static::MARKER_KEY]);
                 $this->fmt->text('array');
                 $this->fmt->emptyGroup('recursion');
                 return;
             }
             // first recursion level detection;
             // this is optional (used to print consistent recursion info)
             foreach ($subject as $key => &$value) {
                 if (!is_array($value)) {
                     continue;
                 }
                 // save current value in a temporary variable
                 $buffer = $value;
                 // assign new value
                 $value = $value !== 1 ? 1 : 2;
                 // if they're still equal, then we have a reference
                 if ($value === $subject) {
                     $value = $buffer;
                     $value[static::MARKER_KEY] = true;
                     $this->evaluate($value);
                     return;
                 }
                 // restoring original value
                 $value = $buffer;
             }
             $this->fmt->text('array');
             $count = count($subject);
             if (!$this->fmt->startGroup($count)) {
                 return;
             }
             $max = max(array_map('static::strLen', array_keys($subject)));
             $subject[static::MARKER_KEY] = true;
             foreach ($subject as $key => &$value) {
                 // ignore our temporary marker
                 if ($key === static::MARKER_KEY) {
                     continue;
                 }
                 if ($this->hasInstanceTimedOut()) {
                     break;
                 }
                 $keyInfo = gettype($key);
                 if ($keyInfo === 'string') {
                     $encoding = static::$env['mbStr'] ? mb_detect_encoding($key) : '';
                     $keyLen = $encoding && $encoding !== 'ASCII' ? static::strLen($key) . '; ' . $encoding : static::strLen($key);
                     $keyInfo = "{$keyInfo}({$keyLen})";
                 } else {
                     $keyLen = strlen($key);
                 }
                 $this->fmt->startRow();
                 $this->fmt->text('key', $key, "Key: {$keyInfo}");
                 $this->fmt->colDiv($max - $keyLen);
                 $this->fmt->sep('=>');
                 $this->fmt->colDiv();
                 $this->evaluate($value, $specialStr);
                 $this->fmt->endRow();
             }
             unset($subject[static::MARKER_KEY]);
             $this->fmt->endGroup();
             return;
             // resource
         // resource
         case 'resource':
             $meta = array();
             $resType = get_resource_type($subject);
             $this->fmt->text('resource', strval($subject));
             if (!static::$config['showResourceInfo']) {
                 return $this->fmt->emptyGroup($resType);
             }
             // @see: http://php.net/manual/en/resource.php
             // need to add more...
             switch ($resType) {
                 // curl extension resource
                 case 'curl':
                     $meta = curl_getinfo($subject);
                     break;
                 case 'FTP Buffer':
                     $meta = array('time_out' => ftp_get_option($subject, FTP_TIMEOUT_SEC), 'auto_seek' => ftp_get_option($subject, FTP_AUTOSEEK));
                     break;
                     // gd image extension resource
                 // gd image extension resource
                 case 'gd':
                     $meta = array('size' => sprintf('%d x %d', imagesx($subject), imagesy($subject)), 'true_color' => imageistruecolor($subject));
                     break;
                 case 'ldap link':
                     $constants = get_defined_constants();
                     array_walk($constants, function ($value, $key) use(&$constants) {
                         if (strpos($key, 'LDAP_OPT_') !== 0) {
                             unset($constants[$key]);
                         }
                     });
                     // this seems to fail on my setup :(
                     unset($constants['LDAP_OPT_NETWORK_TIMEOUT']);
                     foreach (array_slice($constants, 3) as $key => $value) {
                         if (ldap_get_option($subject, (int) $value, $ret)) {
                             $meta[strtolower(substr($key, 9))] = $ret;
                         }
                     }
                     break;
                     // mysql connection (mysql extension is deprecated from php 5.4/5.5)
                 // mysql connection (mysql extension is deprecated from php 5.4/5.5)
                 case 'mysql link':
                 case 'mysql link persistent':
                     $dbs = array();
                     $query = @mysql_list_dbs($subject);
                     while ($row = @mysql_fetch_array($query)) {
                         $dbs[] = $row['Database'];
                     }
                     $meta = array('host' => ltrim(@mysql_get_host_info($subject), 'MySQL host info: '), 'server_version' => @mysql_get_server_info($subject), 'protocol_version' => @mysql_get_proto_info($subject), 'databases' => $dbs);
                     break;
                     // mysql result
                 // mysql result
                 case 'mysql result':
                     while ($row = @mysql_fetch_object($subject)) {
                         $meta[] = (array) $row;
                         if ($this->hasInstanceTimedOut()) {
                             break;
                         }
                     }
                     break;
                     // stream resource (fopen, fsockopen, popen, opendir etc)
                 // stream resource (fopen, fsockopen, popen, opendir etc)
                 case 'stream':
                     $meta = stream_get_meta_data($subject);
                     break;
             }
             if (!$meta) {
                 return $this->fmt->emptyGroup($resType);
             }
             if (!$this->fmt->startGroup($resType)) {
                 return;
             }
             $max = max(array_map('static::strLen', array_keys($meta)));
             foreach ($meta as $key => $value) {
                 $this->fmt->startRow();
                 $this->fmt->text('resourceProp', ucwords(str_replace('_', ' ', $key)));
                 $this->fmt->colDiv($max - static::strLen($key));
                 $this->fmt->sep(':');
                 $this->fmt->colDiv();
                 $this->evaluate($value);
                 $this->fmt->endRow();
             }
             $this->fmt->endGroup();
             return;
             // string
         // string
         case 'string':
             $length = static::strLen($subject);
             $encoding = static::$env['mbStr'] ? mb_detect_encoding($subject) : false;
             $info = $encoding && $encoding !== 'ASCII' ? $length . '; ' . $encoding : $length;
             if ($specialStr) {
                 $this->fmt->sep('"');
                 $this->fmt->text(array('string', 'special'), $subject, "string({$info})");
                 $this->fmt->sep('"');
                 return;
             }
             $this->fmt->text('string', $subject, "string({$info})");
             // advanced checks only if there are 3 characteres or more
             if (static::$config['showStringMatches'] && $length > 2 && trim($subject) !== '') {
                 $isNumeric = is_numeric($subject);
                 // very simple check to determine if the string could match a file path
                 // @note: this part of the code is very expensive
                 $isFile = $length < 2048 && max(array_map('strlen', explode('/', str_replace('\\', '/', $subject)))) < 128 && !preg_match('/[^\\w\\.\\-\\/\\\\:]|\\..*\\.|\\.$|:(?!(?<=^[a-zA-Z]:)[\\/\\\\])/', $subject);
                 if ($isFile) {
                     try {
                         $file = new \SplFileInfo($subject);
                         $flags = array();
                         $perms = $file->getPerms();
                         if (($perms & 0xc000) === 0xc000) {
                             // socket
                             $flags[] = 's';
                         } elseif (($perms & 0xa000) === 0xa000) {
                             // symlink
                             $flags[] = 'l';
                         } elseif (($perms & 0x8000) === 0x8000) {
                             // regular
                             $flags[] = '-';
                         } elseif (($perms & 0x6000) === 0x6000) {
                             // block special
                             $flags[] = 'b';
                         } elseif (($perms & 0x4000) === 0x4000) {
                             // directory
                             $flags[] = 'd';
                         } elseif (($perms & 0x2000) === 0x2000) {
                             // character special
                             $flags[] = 'c';
                         } elseif (($perms & 0x1000) === 0x1000) {
                             // FIFO pipe
                             $flags[] = 'p';
                         } else {
                             // unknown
                             $flags[] = 'u';
                         }
                         // owner
                         $flags[] = $perms & 0x100 ? 'r' : '-';
                         $flags[] = $perms & 0x80 ? 'w' : '-';
                         $flags[] = $perms & 0x40 ? $perms & 0x800 ? 's' : 'x' : ($perms & 0x800 ? 'S' : '-');
                         // group
                         $flags[] = $perms & 0x20 ? 'r' : '-';
                         $flags[] = $perms & 0x10 ? 'w' : '-';
                         $flags[] = $perms & 0x8 ? $perms & 0x400 ? 's' : 'x' : ($perms & 0x400 ? 'S' : '-');
                         // world
                         $flags[] = $perms & 0x4 ? 'r' : '-';
                         $flags[] = $perms & 0x2 ? 'w' : '-';
                         $flags[] = $perms & 0x1 ? $perms & 0x200 ? 't' : 'x' : ($perms & 0x200 ? 'T' : '-');
                         $size = is_dir($subject) ? '' : sprintf(' %.2fK', $file->getSize() / 1024);
                         $this->fmt->startContain('file', true);
                         $this->fmt->text('file', implode('', $flags) . $size);
                         $this->fmt->endContain();
                     } catch (\Exception $e) {
                         $isFile = false;
                     }
                 }
                 // class/interface/function
                 if (!preg_match('/[^\\w+\\\\]/', $subject) && $length < 96) {
                     $isClass = class_exists($subject, false);
                     if ($isClass) {
                         $this->fmt->startContain('class', true);
                         $this->fromReflector(new \ReflectionClass($subject));
                         $this->fmt->endContain();
                     }
                     if (!$isClass && interface_exists($subject, false)) {
                         $this->fmt->startContain('interface', true);
                         $this->fromReflector(new \ReflectionClass($subject));
                         $this->fmt->endContain('interface');
                     }
                     if (function_exists($subject)) {
                         $this->fmt->startContain('function', true);
                         $this->fromReflector(new \ReflectionFunction($subject));
                         $this->fmt->endContain('function');
                     }
                 }
                 // skip serialization/json/date checks if the string appears to be numeric,
                 // or if it's shorter than 5 characters
                 if (!$isNumeric && $length > 4) {
                     // url
                     if (static::$config['showUrls'] && static::$env['curlActive'] && filter_var($subject, FILTER_VALIDATE_URL)) {
                         $ch = curl_init($subject);
                         curl_setopt($ch, CURLOPT_NOBODY, true);
                         curl_exec($ch);
                         $nfo = curl_getinfo($ch);
                         curl_close($ch);
                         if ($nfo['http_code']) {
                             $this->fmt->startContain('url', true);
                             $contentType = explode(';', $nfo['content_type']);
                             $this->fmt->text('url', sprintf('%s:%d %s %.2fms (%d)', !empty($nfo['primary_ip']) ? $nfo['primary_ip'] : null, !empty($nfo['primary_port']) ? $nfo['primary_port'] : null, $contentType[0], $nfo['total_time'], $nfo['http_code']));
                             $this->fmt->endContain();
                         }
                     }
                     // date
                     if ($length < 128 && static::$env['supportsDate'] && !preg_match('/[^A-Za-z0-9.:+\\s\\-\\/]/', $subject)) {
                         try {
                             $date = new \DateTime($subject);
                             $errors = \DateTime::getLastErrors();
                             if ($errors['warning_count'] < 1 && $errors['error_count'] < 1) {
                                 $now = new \Datetime('now');
                                 $nowUtc = new \Datetime('now', new \DateTimeZone('UTC'));
                                 $diff = $now->diff($date);
                                 $map = array('y' => 'yr', 'm' => 'mo', 'd' => 'da', 'h' => 'hr', 'i' => 'min', 's' => 'sec');
                                 $timeAgo = 'now';
                                 foreach ($map as $k => $label) {
                                     if ($diff->{$k} > 0) {
                                         $timeAgo = $diff->format("%R%{$k}{$label}");
                                         break;
                                     }
                                 }
                                 $tz = $date->getTimezone();
                                 $offs = round($tz->getOffset($nowUtc) / 3600);
                                 if ($offs > 0) {
                                     $offs = "+{$offs}";
                                 }
                                 $timeAgo .= (int) $offs !== 0 ? ' ' . sprintf('%s (UTC%s)', $tz->getName(), $offs) : ' UTC';
                                 $this->fmt->startContain('date', true);
                                 $this->fmt->text('date', $timeAgo);
                                 $this->fmt->endContain();
                             }
                         } catch (\Exception $e) {
                             // not a date
                         }
                     }
                     // attempt to detect if this is a serialized string
                     static $unserializing = 0;
                     $isSerialized = $unserializing < 3 && ($subject[$length - 1] === ';' || $subject[$length - 1] === '}') && in_array($subject[0], array('s', 'a', 'O'), true) && ($subject[0] === 's' && $subject[$length - 2] !== '"' || preg_match("/^{$subject[0]}:[0-9]+:/s", $subject)) && ($unserialized = @unserialize($subject)) !== false;
                     if ($isSerialized) {
                         $unserializing++;
                         $this->fmt->startContain('serialized', true);
                         $this->evaluate($unserialized);
                         $this->fmt->endContain();
                         $unserializing--;
                     }
                     // try to find out if it's a json-encoded string;
                     // only do this for json-encoded arrays or objects, because other types have too generic formats
                     static $decodingJson = 0;
                     $isJson = !$isSerialized && $decodingJson < 3 && in_array($subject[0], array('{', '['), true);
                     if ($isJson) {
                         $decodingJson++;
                         $json = json_decode($subject);
                         if ($isJson = json_last_error() === JSON_ERROR_NONE) {
                             $this->fmt->startContain('json', true);
                             $this->evaluate($json);
                             $this->fmt->endContain();
                         }
                         $decodingJson--;
                     }
                     // attempt to match a regex
                     if ($length < 768) {
                         try {
                             $components = $this->splitRegex($subject);
                             if ($components) {
                                 $regex = '';
                                 $this->fmt->startContain('regex', true);
                                 foreach ($components as $component) {
                                     $this->fmt->text('regex-' . key($component), reset($component));
                                 }
                                 $this->fmt->endContain();
                             }
                         } catch (\Exception $e) {
                             // not a regex
                         }
                     }
                 }
             }
             return;
     }
     // if we reached this point, $subject must be an object
     // track objects to detect recursion
     static $hashes = array();
     // hash ID of this object
     $hash = spl_object_hash($subject);
     $recursion = isset($hashes[$hash]);
     // sometimes incomplete objects may be created from string unserialization,
     // if the class to which the object belongs wasn't included until the unserialization stage...
     if ($subject instanceof \__PHP_Incomplete_Class) {
         $this->fmt->text('object');
         $this->fmt->emptyGroup('incomplete');
         return;
     }
     // check cache at this point
     if (!$recursion && $this->fmt->didCache($hash)) {
         static::$debug['cacheHits']++;
         return;
     }
     $reflector = new \ReflectionObject($subject);
     $this->fmt->startContain('class');
     $this->fromReflector($reflector);
     $this->fmt->text('object', ' object');
     $this->fmt->endContain();
     // already been here?
     if ($recursion) {
         return $this->fmt->emptyGroup('recursion');
     }
     $hashes[$hash] = 1;
     $flags = \ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED;
     if (static::$config['showPrivateMembers']) {
         $flags |= \ReflectionProperty::IS_PRIVATE;
     }
     $props = $reflector->getProperties($flags);
     $methods = array();
     if (static::$config['showMethods']) {
         $flags = \ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED;
         if (static::$config['showPrivateMembers']) {
             $flags |= \ReflectionMethod::IS_PRIVATE;
         }
         $methods = $reflector->getMethods($flags);
     }
     $constants = $reflector->getConstants();
     $interfaces = $reflector->getInterfaces();
     $traits = static::$env['is54'] ? $reflector->getTraits() : array();
     $parents = static::getParentClasses($reflector);
     // work-around for https://bugs.php.net/bug.php?id=49154
     // @see http://stackoverflow.com/questions/15672287/strange-behavior-of-reflectiongetproperties-with-numeric-keys
     if (!static::$env['is54']) {
         $props = array_values(array_filter($props, function ($prop) use($subject) {
             return !$prop->isPublic() || property_exists($subject, $prop->name);
         }));
     }
     // no data to display?
     if (!$props && !$methods && !$constants && !$interfaces && !$traits) {
         unset($hashes[$hash]);
         return $this->fmt->emptyGroup();
     }
     if (!$this->fmt->startGroup()) {
         return;
     }
     // show contents for iterators
     if (static::$config['showIteratorContents'] && $reflector->isIterateable()) {
         $itContents = iterator_to_array($subject);
         $this->fmt->sectionTitle(sprintf('Contents (%d)', count($itContents)));
         foreach ($itContents as $key => $value) {
             $keyInfo = gettype($key);
             if ($keyInfo === 'string') {
                 $encoding = static::$env['mbStr'] ? mb_detect_encoding($key) : '';
                 $length = $encoding && $encoding !== 'ASCII' ? static::strLen($key) . '; ' . $encoding : static::strLen($key);
                 $keyInfo = sprintf('%s(%s)', $keyInfo, $length);
             }
             $this->fmt->startRow();
             $this->fmt->text(array('key', 'iterator'), $key, sprintf('Iterator key: %s', $keyInfo));
             $this->fmt->colDiv();
             $this->fmt->sep('=>');
             $this->fmt->colDiv();
             $this->evaluate($value);
             //$this->evaluate($value instanceof \Traversable ? ((count($value) > 0) ? $value : (string)$value) : $value);
             $this->fmt->endRow();
         }
     }
     // display the interfaces this objects' class implements
     if ($interfaces) {
         $items = array();
         $this->fmt->sectionTitle('Implements');
         $this->fmt->startRow();
         $this->fmt->startContain('interfaces');
         $i = 0;
         $count = count($interfaces);
         foreach ($interfaces as $name => $interface) {
             $this->fromReflector($interface);
             if (++$i < $count) {
                 $this->fmt->sep(', ');
             }
         }
         $this->fmt->endContain();
         $this->fmt->endRow();
     }
     // traits this objects' class uses
     if ($traits) {
         $items = array();
         $this->fmt->sectionTitle('Uses');
         $this->fmt->startRow();
         $this->fmt->startContain('traits');
         $i = 0;
         $count = count($traits);
         foreach ($traits as $name => $trait) {
             $this->fromReflector($trait);
             if (++$i < $count) {
                 $this->fmt->sep(', ');
             }
         }
         $this->fmt->endContain();
         $this->fmt->endRow();
     }
     // class constants
     if ($constants) {
         $this->fmt->sectionTitle('Constants');
         $max = max(array_map('static::strLen', array_keys($constants)));
         foreach ($constants as $name => $value) {
             $meta = null;
             $type = array('const');
             foreach ($parents as $parent) {
                 if ($parent->hasConstant($name)) {
                     if ($parent !== $reflector) {
                         $type[] = 'inherited';
                         $meta = array('sub' => array(array('Prototype defined by', $parent->name)));
                     }
                     break;
                 }
             }
             $this->fmt->startRow();
             $this->fmt->sep('::');
             $this->fmt->colDiv();
             $this->fmt->startContain($type);
             $this->fmt->text('name', $name, $meta, $this->linkify($parent, $name));
             $this->fmt->endContain();
             $this->fmt->colDiv($max - static::strLen($name));
             $this->fmt->sep('=');
             $this->fmt->colDiv();
             $this->evaluate($value);
             $this->fmt->endRow();
         }
     }
     // object/class properties
     if ($props) {
         $this->fmt->sectionTitle('Properties');
         $max = 0;
         foreach ($props as $idx => $prop) {
             if (($propNameLen = static::strLen($prop->name)) > $max) {
                 $max = $propNameLen;
             }
         }
         foreach ($props as $idx => $prop) {
             if ($this->hasInstanceTimedOut()) {
                 break;
             }
             $bubbles = array();
             $sourceClass = $prop->getDeclaringClass();
             $inherited = $reflector->getShortName() !== $sourceClass->getShortName();
             $meta = $sourceClass->isInternal() ? null : static::parseComment($prop->getDocComment());
             if ($meta) {
                 if ($inherited) {
                     $meta['sub'] = array(array('Declared in', $sourceClass->getShortName()));
                 }
                 if (isset($meta['tags']['var'][0])) {
                     $meta['left'] = $meta['tags']['var'][0][0];
                 }
                 unset($meta['tags']);
             }
             if ($prop->isProtected() || $prop->isPrivate()) {
                 $prop->setAccessible(true);
             }
             $value = $prop->getValue($subject);
             $this->fmt->startRow();
             $this->fmt->sep($prop->isStatic() ? '::' : '->');
             $this->fmt->colDiv();
             $bubbles = array();
             if ($prop->isProtected()) {
                 $bubbles[] = array('P', 'Protected');
             }
             if ($prop->isPrivate()) {
                 $bubbles[] = array('!', 'Private');
             }
             $this->fmt->bubbles($bubbles);
             $type = array('prop');
             if ($inherited) {
                 $type[] = 'inherited';
             }
             if ($prop->isPrivate()) {
                 $type[] = 'private';
             }
             $this->fmt->colDiv(2 - count($bubbles));
             $this->fmt->startContain($type);
             $this->fmt->text('name', $prop->name, $meta, $this->linkify($prop));
             $this->fmt->endContain();
             $this->fmt->colDiv($max - static::strLen($prop->name));
             $this->fmt->sep('=');
             $this->fmt->colDiv();
             $this->evaluate($value);
             $this->fmt->endRow();
         }
     }
     // class methods
     if ($methods && !$this->hasInstanceTimedOut()) {
         $this->fmt->sectionTitle('Methods');
         foreach ($methods as $idx => $method) {
             $this->fmt->startRow();
             $this->fmt->sep($method->isStatic() ? '::' : '->');
             $this->fmt->colDiv();
             $bubbles = array();
             if ($method->isAbstract()) {
                 $bubbles[] = array('A', 'Abstract');
             }
             if ($method->isFinal()) {
                 $bubbles[] = array('F', 'Final');
             }
             if ($method->isProtected()) {
                 $bubbles[] = array('P', 'Protected');
             }
             if ($method->isPrivate()) {
                 $bubbles[] = array('!', 'Private');
             }
             $this->fmt->bubbles($bubbles);
             $this->fmt->colDiv(4 - count($bubbles));
             // is this method inherited?
             $inherited = $reflector->getShortName() !== $method->getDeclaringClass()->getShortName();
             $type = array('method');
             if ($inherited) {
                 $type[] = 'inherited';
             }
             if ($method->isPrivate()) {
                 $type[] = 'private';
             }
             $this->fmt->startContain($type);
             $name = $method->name;
             if ($method->returnsReference()) {
                 $name = "&{$name}";
             }
             $this->fromReflector($method, $name, $reflector);
             $paramCom = $method->isInternal() ? array() : static::parseComment($method->getDocComment(), 'tags');
             $paramCom = empty($paramCom['param']) ? array() : $paramCom['param'];
             $paramCount = $method->getNumberOfParameters();
             $this->fmt->sep('(');
             // process arguments
             foreach ($method->getParameters() as $idx => $parameter) {
                 $meta = null;
                 $paramName = "\${$parameter->name}";
                 $optional = $parameter->isOptional();
                 $variadic = static::$env['is56'] && $parameter->isVariadic();
                 if ($parameter->isPassedByReference()) {
                     $paramName = "&{$paramName}";
                 }
                 if ($variadic) {
                     $paramName = "...{$paramName}";
                 }
                 $type = array('param');
                 if ($optional) {
                     $type[] = 'optional';
                 }
                 $this->fmt->startContain($type);
                 // attempt to build meta
                 foreach ($paramCom as $tag) {
                     list($pcTypes, $pcName, $pcDescription) = $tag;
                     if ($pcName !== $paramName) {
                         continue;
                     }
                     $meta = array('title' => $pcDescription);
                     if ($pcTypes) {
                         $meta['left'] = $pcTypes;
                     }
                     break;
                 }
                 try {
                     $paramClass = $parameter->getClass();
                 } catch (\Exception $e) {
                     // @see https://bugs.php.net/bug.php?id=32177&edit=1
                 }
                 if (!empty($paramClass)) {
                     $this->fmt->startContain('hint');
                     $this->fromReflector($paramClass, $paramClass->name);
                     $this->fmt->endContain();
                     $this->fmt->sep(' ');
                 }
                 if ($parameter->isArray()) {
                     $this->fmt->text('hint', 'array');
                     $this->fmt->sep(' ');
                 }
                 $this->fmt->text('name', $paramName, $meta);
                 if ($optional) {
                     $paramValue = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null;
                     $this->fmt->sep(' = ');
                     if (static::$env['is546'] && !$parameter->getDeclaringFunction()->isInternal() && $parameter->isDefaultValueConstant()) {
                         $this->fmt->text('constant', $parameter->getDefaultValueConstantName(), 'Constant');
                     } else {
                         $this->evaluate($paramValue, true);
                     }
                 }
                 $this->fmt->endContain();
                 if ($idx < $paramCount - 1) {
                     $this->fmt->sep(', ');
                 }
             }
             $this->fmt->sep(')');
             $this->fmt->endContain();
             $this->fmt->endRow();
         }
     }
     unset($hashes[$hash]);
     $this->fmt->endGroup();
     $this->fmt->cacheLock($hash);
 }