diff --git a/lib/SimpleSAML/Auth/LDAP.php b/lib/SimpleSAML/Auth/LDAP.php
index b28d060431970382b93001874df99abbd391bc98..baec408e4af58bc55a4f73dbf9b67969a27fa26d 100644
--- a/lib/SimpleSAML/Auth/LDAP.php
+++ b/lib/SimpleSAML/Auth/LDAP.php
@@ -12,7 +12,7 @@ define('ERR_AS_ATTRIBUTE', 6);
 
 // not defined in earlier PHP versions
 if (!defined('LDAP_OPT_DIAGNOSTIC_MESSAGE')) {
-	define('LDAP_OPT_DIAGNOSTIC_MESSAGE', 0x0032);
+    define('LDAP_OPT_DIAGNOSTIC_MESSAGE', 0x0032);
 }
 
 /**
@@ -25,344 +25,357 @@ if (!defined('LDAP_OPT_DIAGNOSTIC_MESSAGE')) {
 class SimpleSAML_Auth_LDAP {
 
 
-	/**
-	 * LDAP link identifier.
-	 *
-	 * @var resource
-	 */
-	protected $ldap = null;
-
-	/**
-	 * LDAP user: authz_id if SASL is in use, binding dn otherwise
-	 */
-	protected $authz_id = null;
-
-	/**
-	 * Timeout value, in seconds.
-	 *
-	 * @var int
-	 */
-	protected $timeout = 0;
-
-	/**
-	 * Private constructor restricts instantiation to getInstance().
-	 *
-	 * @param string $hostname
-	 * @param bool $enable_tls
-	 * @param bool $debug
-	 * @param int $timeout
-	 * @param int $port
-	 * @param bool $referrals
-	 */
-	// TODO: Flesh out documentation.
-	public function __construct($hostname, $enable_tls = TRUE, $debug = FALSE, $timeout = 0, $port = 389, $referrals = TRUE) {
-
-		// Debug.
-		SimpleSAML_Logger::debug('Library - LDAP __construct(): Setup LDAP with ' .
-			'host=\'' . $hostname .
-			'\', tls=' . var_export($enable_tls, true) .
-			', debug=' . var_export($debug, true) .
-			', timeout=' . var_export($timeout, true) .
-			', referrals=' . var_export($referrals, true));
-
-		/*
-		 * Set debug level before calling connect. Note that this passes
-		 * NULL to ldap_set_option, which is an undocumented feature.
-		 *
-		 * OpenLDAP 2.x.x or Netscape Directory SDK x.x needed for this option.
-		 */
-		if ($debug && !ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7))
-			SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set debug level (LDAP_OPT_DEBUG_LEVEL) to 7');
-
-		/*
-		 * Prepare a connection for to this LDAP server. Note that this function
-		 * doesn't actually connect to the server.
-		 */
-		$this->ldap = @ldap_connect($hostname, $port);
-		if ($this->ldap == FALSE)
-			throw $this->makeException('Library - LDAP __construct(): Unable to connect to \'' . $hostname . '\'', ERR_INTERNAL);
-
-		/* Enable LDAP protocol version 3. */
-		if (!@ldap_set_option($this->ldap, LDAP_OPT_PROTOCOL_VERSION, 3))
-			throw $this->makeException('Library - LDAP __construct(): Failed to set LDAP Protocol version (LDAP_OPT_PROTOCOL_VERSION) to 3', ERR_INTERNAL);
-
-		/* Set referral option */
-		if (!@ldap_set_option($this->ldap, LDAP_OPT_REFERRALS, $referrals))
-			throw $this->makeException('Library - LDAP __construct(): Failed to set LDAP Referrals (LDAP_OPT_REFERRALS) to '.$referrals, ERR_INTERNAL);
-
-		// Set timeouts, if supported.
-		// (OpenLDAP 2.x.x or Netscape Directory SDK x.x needed).
-		$this->timeout = $timeout;
-		if ($timeout > 0) {
-			if (!@ldap_set_option($this->ldap, LDAP_OPT_NETWORK_TIMEOUT, $timeout))
-				SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_NETWORK_TIMEOUT) to ' . $timeout);
-			if (!@ldap_set_option($this->ldap, LDAP_OPT_TIMELIMIT, $timeout))
-				SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_TIMELIMIT) to ' . $timeout);
-		}
-
-		// Enable TLS, if needed.
-		if (stripos($hostname, "ldaps:") === FALSE and $enable_tls)
-			if (!@ldap_start_tls($this->ldap))
-				throw $this->makeException('Library - LDAP __construct(): Unable to force TLS', ERR_INTERNAL);
-
-	}
-	
-	
-	/**
-	 * 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 = 0x00;
-		
-		// 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);
-			}
-		}
-	}
-
-
-	/**
-	 * Search for DN from a single base.
-	 *
-	 * @param string $base
-	 * Indication of root of subtree to search
-	 * @param string|array $attribute
-	 * The attribute name(s) to search for.
-	 * @param string $value
-	 * The attribute value to search for.
-	 * @return string
-	 * The DN of the resulting found element.
-	 * @throws SimpleSAML_Error_Exception if:
-	 * - Attribute parameter is wrong type
-	 * @throws SimpleSAML_Error_AuthSource if:
-	 * - Not able to connect to LDAP server
-	 * - False search result
-	 * - Count return false
-	 * - Searche found more than one result
-	 * - Failed to get first entry from result
-	 * - Failed to get DN for entry
-	 * @throws SimpleSAML_Error_UserNotFound if:
-	 * - Zero entries was found
-	 */
-	private function search($base, $attribute, $value) {
-
-		// Create the search filter.
-		$attribute = self::escape_filter_value($attribute, FALSE);
-		$value = self::escape_filter_value($value);
-		$filter = '';
-		foreach ($attribute AS $attr) {
-			$filter .= '(' . $attr . '=' . $value. ')';
-		}
-		$filter = '(|' . $filter . ')';
-
-		// Search using generated filter.
-		SimpleSAML_Logger::debug('Library - LDAP search(): Searching base \'' . $base . '\' for \'' . $filter . '\'');
-		// TODO: Should aliases be dereferenced?
-		$result = @ldap_search($this->ldap, $base, $filter, array(), 0, 0, $this->timeout);
-		if ($result === FALSE){
-			throw $this->makeException('Library - LDAP search(): Failed search on base \'' . $base . '\' for \'' . $filter . '\'');
-		}
-
-		// Sanity checks on search results.
-		$count = @ldap_count_entries($this->ldap, $result);
-		if($count === FALSE){
-			throw $this->makeException('Library - LDAP search(): Failed to get number of entries returned');
-		}elseif($count > 1){
-			// More than one entry is found. External error
-			throw $this->makeException('Library - LDAP search(): Found ' . $count . ' entries searching base \'' . $base . '\' for \'' . $filter . '\'', ERR_AS_DATA_INCONSIST);
-		}elseif($count === 0){
-			// No entry is fond => wrong username is given (or not registered in the catalogue). User error
-			throw $this->makeException('Library - LDAP search(): Found no entries searching base \'' . $base . '\' for \'' . $filter . '\'', ERR_NO_USER);
-		}
-		
-
-		// Resolve the DN from the search result.
-		$entry = @ldap_first_entry($this->ldap, $result);
-		if ($entry === FALSE)
-			throw $this->makeException('Library - LDAP search(): Unable to retrieve result after searching base \'' . $base . '\' for \'' . $filter . '\'');
-		$dn = @ldap_get_dn($this->ldap, $entry);
-		if ($dn === FALSE)
-			throw $this->makeException('Library - LDAP search(): Unable to get DN after searching base \'' . $base . '\' for \'' . $filter . '\'');
-		// FIXME: Are we now shure, if no excepton hawe been thrown, that we are returning av DN?
-		return $dn;
-	}
-
-
-	/**
-	 * Search for a DN.
-	 *
-	 * @param string|array $base
-	 * The base, or bases, which to search from.
-	 * @param string|array $attribute
-	 * The attribute name(s) searched for.
-	 * @param string $value
-	 * The attribute value searched for.
-	 * @param bool $allowZeroHits
-	 * Determines if the method will throw an exception if no hits are found.
-	 * Defaults to FALSE.
-	 * @return string
-	 * The DN of the matching element, if found. If no element was found and
-	 * $allowZeroHits is set to FALSE, an exception will be thrown; otherwise
-	 * NULL will be returned.
-	 * @throws SimpleSAML_Error_AuthSource if:
-	 * - LDAP search encounter some problems when searching cataloge
-	 * - Not able to connect to LDAP server
-	 * @throws SimpleSAML_Error_UserNotFound if:
-	 * - $allowZeroHits er TRUE and no result is found
-	 *
-	 */
-	public function searchfordn($base, $attribute, $value, $allowZeroHits = FALSE) {
-
-		// Traverse all search bases, returning DN if found.
-		$bases = SimpleSAML\Utils\Arrays::arrayize($base);
-		$result = NULL;
-		foreach ($bases AS $current) {
-			try {
-				// Single base search.
-				$result = $this->search($current, $attribute, $value);
-				// We don't hawe to look any futher if user is found
-				if (!empty($result))
-					return $result;
-			// If search failed, attempt the other base DNs.
-			}catch(SimpleSAML_Error_UserNotFound $e){
-				// Just continue searching
-			}
-		}
-		// Decide what to do for zero entries.
-		SimpleSAML_Logger::debug('Library - LDAP searchfordn(): No entries found');
-		if($allowZeroHits){
-			// Zero hits allowed.
-			return NULL;
-		} else {
-			// Zero hits not allowed.
-			throw $this->makeException('Library - LDAP searchfordn(): LDAP search returned zero entries for filter \'(' . $attribute . ' = ' . $value . ')\' on base(s) \'(' . join(' & ', $bases) . ')\'', 2);
-		}
-	}
-
-
-	/**
-	 * This method was created specifically for the ldap:AttributeAddUsersGroups->searchActiveDirectory()
-	 * method, but could be used for other LDAP search needs. It will search LDAP and return all the entries.
-	 *
-	 * @throws Exception
-	 * @param string|array $bases
-	 * @param string|array $filters Array of 'attribute' => 'values' to be combined into the filter, or a raw filter string
-	 * @param string|array $attributes Array of attributes requested from LDAP
-	 * @param bool $and If multiple filters defined, then either bind them with & or |
-	 * @param bool $escape Weather to escape the filter values or not
-	 * @return array
-	 */
-	public function searchformultiple($bases, $filters, $attributes = array(), $and = TRUE, $escape = TRUE) {
-
-		// Escape the filter values, if requested
-		if ($escape) {
-			$filters = $this->escape_filter_value($filters, FALSE);
-		}
-
-		// Build search filter
-		$filter = '';
-		if (is_array($filters)) {
-			foreach ($filters as $attribute => $value) {
-				$filter .= "($attribute=$value)";
-			}
-			if (count($filters) > 1) {
-				$filter = ($and ? '(&' : '(|') . $filter . ')';
-			}
-		} elseif (is_string($filters)) {
-			$filter = $filters;
-		}
-
-		// Verify filter was created
-		if ($filter == '' || $filter == '(=)') {
-			throw $this->makeException('ldap:LdapConnection->search_manual : No search filters defined', ERR_INTERNAL);
-		}
-
-		// Verify at least one base was passed
-		$bases = (array) $bases;
-		if (empty($bases)) {
-			throw $this->makeException('ldap:LdapConnection->search_manual : No base DNs were passed', ERR_INTERNAL);
-		}
-
-		// Search each base until result is found
-		$result = FALSE;
-		foreach ($bases as $base) {
-			$result = @ldap_search($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout);
-			if ($result !== FALSE) break;
-		}
-
-		// Verify that a result was found in one of the bases
-		if ($result === FALSE) {
-			throw $this->makeException(
-				'ldap:LdapConnection->search_manual : Failed to search LDAP using base(s) [' .
-				implode('; ', $bases) . '] with filter [' . $filter . ']. LDAP error [' .
-				ldap_error($this->ldap) . ']'
-			);
-		} elseif (@ldap_count_entries($this->ldap, $result) < 1) {
-			throw $this->makeException(
-				'ldap:LdapConnection->search_manual : No entries found in LDAP using base(s) [' .
-				implode('; ', $bases) . '] with filter [' . $filter . ']',
-				ERR_NO_USER
-			);
-		}
-
-		// Get all results
-		$results = ldap_get_entries($this->ldap, $result);
-		if ($results === FALSE) {
-			throw $this->makeException(
-				'ldap:LdapConnection->search_manual : Unable to retrieve entries from search results'
-			);
-		}
+    /**
+     * LDAP link identifier.
+     *
+     * @var resource
+     */
+    protected $ldap = null;
+
+    /**
+     * LDAP user: authz_id if SASL is in use, binding dn otherwise
+     */
+    protected $authz_id = null;
+
+    /**
+     * Timeout value, in seconds.
+     *
+     * @var int
+     */
+    protected $timeout = 0;
+
+    /**
+     * Private constructor restricts instantiation to getInstance().
+     *
+     * @param string $hostname
+     * @param bool $enable_tls
+     * @param bool $debug
+     * @param int $timeout
+     * @param int $port
+     * @param bool $referrals
+     */
+    // TODO: Flesh out documentation.
+    public function __construct($hostname, $enable_tls = TRUE, $debug = FALSE, $timeout = 0, $port = 389, $referrals = TRUE) {
+
+        // Debug.
+        SimpleSAML_Logger::debug('Library - LDAP __construct(): Setup LDAP with ' .
+                        'host=\'' . $hostname .
+                        '\', tls=' . var_export($enable_tls, true) .
+                        ', debug=' . var_export($debug, true) .
+                        ', timeout=' . var_export($timeout, true) .
+                        ', referrals=' . var_export($referrals, true));
+
+        /*
+         * Set debug level before calling connect. Note that this passes
+         * NULL to ldap_set_option, which is an undocumented feature.
+         *
+         * OpenLDAP 2.x.x or Netscape Directory SDK x.x needed for this option.
+         */
+        if ($debug && !ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7)) {
+                SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set debug level (LDAP_OPT_DEBUG_LEVEL) to 7');
+        }
+
+        /*
+         * Prepare a connection for to this LDAP server. Note that this function
+         * doesn't actually connect to the server.
+         */
+        $this->ldap = @ldap_connect($hostname, $port);
+        if ($this->ldap === FALSE) {
+            throw $this->makeException('Library - LDAP __construct(): Unable to connect to \'' . $hostname . '\'', ERR_INTERNAL);
+        }
+
+        /* Enable LDAP protocol version 3. */
+        if (!@ldap_set_option($this->ldap, LDAP_OPT_PROTOCOL_VERSION, 3)) {
+            throw $this->makeException('Library - LDAP __construct(): Failed to set LDAP Protocol version (LDAP_OPT_PROTOCOL_VERSION) to 3', ERR_INTERNAL);
+        }
+
+        /* Set referral option */
+        if (!@ldap_set_option($this->ldap, LDAP_OPT_REFERRALS, $referrals)) {
+            throw $this->makeException('Library - LDAP __construct(): Failed to set LDAP Referrals (LDAP_OPT_REFERRALS) to '.$referrals, ERR_INTERNAL);
+        }
+
+        // Set timeouts, if supported.
+        // (OpenLDAP 2.x.x or Netscape Directory SDK x.x needed).
+        $this->timeout = $timeout;
+        if ($timeout > 0) {
+            if (!@ldap_set_option($this->ldap, LDAP_OPT_NETWORK_TIMEOUT, $timeout)) {
+                SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_NETWORK_TIMEOUT) to ' . $timeout);
+            }
+            if (!@ldap_set_option($this->ldap, LDAP_OPT_TIMELIMIT, $timeout)) {
+                SimpleSAML_Logger::warning('Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_TIMELIMIT) to ' . $timeout);
+            }
+        }
+
+        // Enable TLS, if needed.
+        if (stripos($hostname, "ldaps:") === FALSE and $enable_tls) {
+            if (!@ldap_start_tls($this->ldap)) {
+                throw $this->makeException('Library - LDAP __construct(): Unable to force TLS', ERR_INTERNAL);
+            }
+        }
+    }
+
+
+    /**
+     * 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 = 0x00;
+
+        // 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);
+            }
+        }
+    }
+
+
+    /**
+     * Search for DN from a single base.
+     *
+     * @param string $base
+     * Indication of root of subtree to search
+     * @param string|array $attribute
+     * The attribute name(s) to search for.
+     * @param string $value
+     * The attribute value to search for.
+     * @return string
+     * The DN of the resulting found element.
+     * @throws SimpleSAML_Error_Exception if:
+     * - Attribute parameter is wrong type
+     * @throws SimpleSAML_Error_AuthSource if:
+     * - Not able to connect to LDAP server
+     * - False search result
+     * - Count return false
+     * - Searche found more than one result
+     * - Failed to get first entry from result
+     * - Failed to get DN for entry
+     * @throws SimpleSAML_Error_UserNotFound if:
+     * - Zero entries was found
+     */
+    private function search($base, $attribute, $value) {
+
+        // Create the search filter.
+        $attribute = self::escape_filter_value($attribute, FALSE);
+        $value = self::escape_filter_value($value);
+        $filter = '';
+        foreach ($attribute AS $attr) {
+            $filter .= '(' . $attr . '=' . $value. ')';
+        }
+        $filter = '(|' . $filter . ')';
+
+        // Search using generated filter.
+        SimpleSAML_Logger::debug('Library - LDAP search(): Searching base \'' . $base . '\' for \'' . $filter . '\'');
+        // TODO: Should aliases be dereferenced?
+        $result = @ldap_search($this->ldap, $base, $filter, array(), 0, 0, $this->timeout);
+        if ($result === FALSE) {
+            throw $this->makeException('Library - LDAP search(): Failed search on base \'' . $base . '\' for \'' . $filter . '\'');
+        }
+
+        // Sanity checks on search results.
+        $count = @ldap_count_entries($this->ldap, $result);
+        if ($count === FALSE) {
+            throw $this->makeException('Library - LDAP search(): Failed to get number of entries returned');
+        } elseif ($count > 1) {
+            // More than one entry is found. External error
+            throw $this->makeException('Library - LDAP search(): Found ' . $count . ' entries searching base \'' . $base . '\' for \'' . $filter . '\'', ERR_AS_DATA_INCONSIST);
+        } elseif ($count === 0) {
+            // No entry is fond => wrong username is given (or not registered in the catalogue). User error
+            throw $this->makeException('Library - LDAP search(): Found no entries searching base \'' . $base . '\' for \'' . $filter . '\'', ERR_NO_USER);
+        }
+
+
+        // Resolve the DN from the search result.
+        $entry = @ldap_first_entry($this->ldap, $result);
+        if ($entry === FALSE) {
+            throw $this->makeException('Library - LDAP search(): Unable to retrieve result after searching base \'' . $base . '\' for \'' . $filter . '\'');
+        }
+        $dn = @ldap_get_dn($this->ldap, $entry);
+        if ($dn === FALSE) {
+            throw $this->makeException('Library - LDAP search(): Unable to get DN after searching base \'' . $base . '\' for \'' . $filter . '\'');
+        }
+        // FIXME: Are we now sure, if no excepton has been thrown, that we are returning a DN?
+        return $dn;
+    }
+
+
+    /**
+     * Search for a DN.
+     *
+     * @param string|array $base
+     * The base, or bases, which to search from.
+     * @param string|array $attribute
+     * The attribute name(s) searched for.
+     * @param string $value
+     * The attribute value searched for.
+     * @param bool $allowZeroHits
+     * Determines if the method will throw an exception if no hits are found.
+     * Defaults to FALSE.
+     * @return string
+     * The DN of the matching element, if found. If no element was found and
+     * $allowZeroHits is set to FALSE, an exception will be thrown; otherwise
+     * NULL will be returned.
+     * @throws SimpleSAML_Error_AuthSource if:
+     * - LDAP search encounter some problems when searching cataloge
+     * - Not able to connect to LDAP server
+     * @throws SimpleSAML_Error_UserNotFound if:
+     * - $allowZeroHits er TRUE and no result is found
+     *
+     */
+    public function searchfordn($base, $attribute, $value, $allowZeroHits = FALSE) {
+
+        // Traverse all search bases, returning DN if found.
+        $bases = SimpleSAML\Utils\Arrays::arrayize($base);
+        $result = NULL;
+        foreach ($bases AS $current) {
+            try {
+                // Single base search.
+                $result = $this->search($current, $attribute, $value);
+                // We don't hawe to look any futher if user is found
+                if (!empty($result)) {
+                    return $result;
+                }
+                // If search failed, attempt the other base DNs.
+            } catch (SimpleSAML_Error_UserNotFound $e) {
+                // Just continue searching
+            }
+        }
+        // Decide what to do for zero entries.
+        SimpleSAML_Logger::debug('Library - LDAP searchfordn(): No entries found');
+        if ($allowZeroHits) {
+            // Zero hits allowed.
+            return NULL;
+        } else {
+            // Zero hits not allowed.
+            throw $this->makeException('Library - LDAP searchfordn(): LDAP search returned zero entries for filter \'(' .
+                $attribute . ' = ' . $value . ')\' on base(s) \'(' . join(' & ', $bases) . ')\'', 2);
+        }
+    }
+
+
+    /**
+     * This method was created specifically for the ldap:AttributeAddUsersGroups->searchActiveDirectory()
+     * method, but could be used for other LDAP search needs. It will search LDAP and return all the entries.
+     *
+     * @throws Exception
+     * @param string|array $bases
+     * @param string|array $filters Array of 'attribute' => 'values' to be combined into the filter, or a raw filter string
+     * @param string|array $attributes Array of attributes requested from LDAP
+     * @param bool $and If multiple filters defined, then either bind them with & or |
+     * @param bool $escape Weather to escape the filter values or not
+     * @return array
+     */
+    public function searchformultiple($bases, $filters, $attributes = array(), $and = TRUE, $escape = TRUE) {
+
+        // Escape the filter values, if requested
+        if ($escape) {
+            $filters = $this->escape_filter_value($filters, FALSE);
+        }
+
+        // Build search filter
+        $filter = '';
+        if (is_array($filters)) {
+            foreach ($filters as $attribute => $value) {
+                $filter .= "($attribute=$value)";
+            }
+            if (count($filters) > 1) {
+                $filter = ($and ? '(&' : '(|') . $filter . ')';
+            }
+        } elseif (is_string($filters)) {
+            $filter = $filters;
+        }
+
+        // Verify filter was created
+        if ($filter == '' || $filter == '(=)') {
+            throw $this->makeException('ldap:LdapConnection->search_manual : No search filters defined', ERR_INTERNAL);
+        }
+
+        // Verify at least one base was passed
+        $bases = (array) $bases;
+        if (empty($bases)) {
+            throw $this->makeException('ldap:LdapConnection->search_manual : No base DNs were passed', ERR_INTERNAL);
+        }
+
+        // Search each base until result is found
+        $result = FALSE;
+        foreach ($bases as $base) {
+            $result = @ldap_search($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout);
+            if ($result !== FALSE) {
+            	break;
+            }
+        }
+
+        // Verify that a result was found in one of the bases
+        if ($result === FALSE) {
+            throw $this->makeException(
+                'ldap:LdapConnection->search_manual : Failed to search LDAP using base(s) [' .
+                implode('; ', $bases) . '] with filter [' . $filter . ']. LDAP error [' .
+                ldap_error($this->ldap) . ']'
+            );
+        } elseif (@ldap_count_entries($this->ldap, $result) < 1) {
+            throw $this->makeException(
+                'ldap:LdapConnection->search_manual : No entries found in LDAP using base(s) [' .
+                implode('; ', $bases) . '] with filter [' . $filter . ']',
+                ERR_NO_USER
+            );
+        }
+
+        // Get all results
+        $results = ldap_get_entries($this->ldap, $result);
+        if ($results === FALSE) {
+            throw $this->makeException(
+                'ldap:LdapConnection->search_manual : Unable to retrieve entries from search results'
+            );
+        }
 
         // parse each entry and process its attributes
         for ($i = 0; $i < $results['count']; $i++) {
@@ -383,328 +396,345 @@ class SimpleSAML_Auth_LDAP {
             }
         }
 
-		// Remove the count and return
-		unset($results['count']);
-		return $results;
-	}
-
-
-	/**
-	 * Bind to LDAP with a specific DN and password. Simple wrapper around
-	 * ldap_bind() with some additional logging.
-	 *
-	 * @param string $dn
-	 * The DN used.
-	 * @param string $password
-	 * The password used.
-	 * @param array $sasl_args
-	 * Array of SASL options for SASL bind
-	 * @return bool
-	 * Returns TRUE if successful, FALSE if
-	 * LDAP_INVALID_CREDENTIALS, LDAP_X_PROXY_AUTHZ_FAILURE,
-	 * LDAP_INAPPROPRIATE_AUTH, LDAP_INSUFFICIENT_ACCESS
-	 * @throws SimpleSAML_Error_Exception on other errors
-	 */
-	public function bind($dn, $password, array $sasl_args = NULL) {
-		$authz_id = null;
-
-		if ($sasl_args != NULL) {
-			if (!function_exists('ldap_sasl_bind')) {
-				$ex_msg = 'Library - missing SASL support';
-				throw $this->makeException($ex_msg);
-			}
-
-			// SASL Bind, with error handling.
-			$authz_id = $sasl_args['authz_id'];
-			$error = @ldap_sasl_bind($this->ldap, $dn, $password,
-						 $sasl_args['mech'],
-						 $sasl_args['realm'],
-						 $sasl_args['authc_id'],
-						 $sasl_args['authz_id'],
-						 $sasl_args['props']);
-		} else {
-			// Simple Bind, with error handling.
-			$authz_id = $dn;
-			$error = @ldap_bind($this->ldap, $dn, $password);
-		}
-
-		if ($error === TRUE) {
-			// Good.
-			$this->authz_id = $authz_id;
-			SimpleSAML_Logger::debug('Library - LDAP bind(): Bind successful with DN \'' . $dn . '\'');
-			return TRUE;
-
-		}
-
-		/* Handle errors
-		 * LDAP_INVALID_CREDENTIALS
-		 * LDAP_INSUFFICIENT_ACCESS */
-		switch(ldap_errno($this->ldap)) {
-		case 32:	/* LDAP_NO_SUCH_OBJECT */
-		case 47:	/* LDAP_X_PROXY_AUTHZ_FAILURE */
-		case 48:	/* LDAP_INAPPROPRIATE_AUTH */
-		case 49:	/* LDAP_INVALID_CREDENTIALS */
-		case 50:	/* LDAP_INSUFFICIENT_ACCESS */
-			return FALSE;
-			break;
-		default;
-			break;
-		}
-
-		// Bad.
-		throw $this->makeException('Library - LDAP bind(): Bind failed with DN \'' . $dn . '\'');
-
-	}
-
-
-	/**
-	 * Applies an LDAP option to the current connection.
-	 *
-	 * @throws Exception
-	 * @param $option
-	 * @param $value
-	 * @return void
-	 */
-	public function setOption($option, $value) {
-
-		// Attempt to set the LDAP option
-		if (!@ldap_set_option($this->ldap, $option, $value)) {
-			throw $this->makeException(
-				'ldap:LdapConnection->setOption : Failed to set LDAP option [' .
-				$option . '] with the value [' . $value . '] error: ' . ldap_error($this->ldap),
-				ERR_INTERNAL
-			);
-		}
-
-		// Log debug message
-		SimpleSAML_Logger::debug(
-			'ldap:LdapConnection->setOption : Set the LDAP option [' .
-			$option . '] with the value [' . $value . ']'
-		);
-	}
-
-
-	/**
-	 * Search a given DN for attributes, and return the resulting associative
-	 * array.
-	 *
-	 * @param string $dn
-	 * The DN of an element.
-	 * @param string|array $attributes
-	 * The names of the attribute(s) to retrieve. Defaults to NULL; that is,
-	 * all available attributes. Note that this is not very effective.
-	 * @param int $maxsize
-	 * The maximum size of any attribute's value(s). If exceeded, the attribute
-	 * will not be returned.
-	 * @return array
-	 * The array of attributes and their values.
-	 * @see http://no.php.net/manual/en/function.ldap-read.php
-	 */
-	public function getAttributes($dn, $attributes = NULL, $maxsize = NULL) {
-
-		// Preparations, including a pretty debug message...
-		$description = 'all attributes';
-		if (is_array($attributes)) {
-			$description = '\'' . join(',', $attributes) . '\'';
-		} else
-			// Get all attributes...
-			// TODO: Verify that this originally was the intended behaviour. Could $attributes be a string?
-			$attributes = array();
-		SimpleSAML_Logger::debug('Library - LDAP getAttributes(): Getting ' . $description . ' from DN \'' . $dn . '\'');
-
-		// Attempt to get attributes.
-		// TODO: Should aliases be dereferenced?
-		$result = @ldap_read($this->ldap, $dn, 'objectClass=*', $attributes, 0, 0, $this->timeout);
-		if ($result === false)
-			throw $this->makeException('Library - LDAP getAttributes(): Failed to get attributes from DN \'' . $dn . '\'');
-		$entry = @ldap_first_entry($this->ldap, $result);
-		if ($entry === false)
-			throw $this->makeException('Library - LDAP getAttributes(): Could not get first entry from DN \'' . $dn . '\'');
-		$attributes = @ldap_get_attributes($this->ldap, $entry);  // Recycling $attributes... Possibly bad practice.
-		if ($attributes === false)
-			throw $this->makeException('Library - LDAP getAttributes(): Could not get attributes of first entry from DN \'' . $dn . '\'');
-
-		// Parsing each found attribute into our result set.
-		$result = array();  // Recycling $result... Possibly bad practice.
-		for ($i = 0; $i < $attributes['count']; $i++) {
-
-			// Ignore attributes that exceed the maximum allowed size.
-			$name = $attributes[$i];
-			$attribute = $attributes[$name];
-
-			// Deciding whether to base64 encode.
-			$values = array();
-			for ($j = 0; $j < $attribute['count']; $j++) {
-				$value = $attribute[$j];
-
-				if (!empty($maxsize) && strlen($value) >= $maxsize) {
-					// Ignoring and warning.
-					SimpleSAML_Logger::warning('Library - LDAP getAttributes(): Attribute \'' .
-						$name . '\' exceeded maximum allowed size by ' + ($maxsize - strlen($value)));
-					continue;
-				}
-
-				// Base64 encode binary attributes.
-				if (strtolower($name) === 'jpegphoto' || strtolower($name) === 'objectguid') {
-					$values[] = base64_encode($value);
-				} else
-					$values[] = $value;
-
-			}
-
-			// Adding.
-			$result[$name] = $values;
-
-		}
-
-		// We're done.
-		SimpleSAML_Logger::debug('Library - LDAP getAttributes(): Found attributes \'(' . join(',', array_keys($result)) . ')\'');
-		return $result;
-	}
-
-
-	/**
-	 * Enter description here...
-	 *
-	 * @param string $config
-	 * @param string $username
-	 * @param string $password
-	 * @return array|bool
-	 */
-	// TODO: Documentation; only cleared up exception/log messages.
-	public function validate($config, $username, $password = null) {
-
-		/* Escape any characters with a special meaning in LDAP. The following
-		 * characters have a special meaning (according to RFC 2253):
-		 * ',', '+', '"', '\', '<', '>', ';', '*'
-		 * These characters are escaped by prefixing them with '\'.
-		 */
-		$username = addcslashes($username, ',+"\\<>;*');
-		$password = addcslashes($password, ',+"\\<>;*');
-
-		if (isset($config['priv_user_dn']))
-		    $this->bind($config['priv_user_dn'], $config['priv_user_pw']);
-		if (isset($config['dnpattern'])) {
-			$dn = str_replace('%username%', $username, $config['dnpattern']);
-		} else {
-			$dn = $this->searchfordn($config['searchbase'], $config['searchattributes'], $username);
-		}
-
-		if ($password !== null) { /* checking users credentials ... assuming below that she may read her own attributes ... */
-			if (!$this->bind($dn, $password)) {
-				SimpleSAML_Logger::info('Library - LDAP validate(): Failed to authenticate \''. $username . '\' using DN \'' . $dn . '\'');
-				return FALSE;
-			}
-		}
-
-		/*
-		 * Retrieve attributes from LDAP
-		 */
-		$attributes = $this->getAttributes($dn, $config['attributes']);
-		return $attributes;
-
-	}
-
-
-	/**
-	 * Borrowed function from PEAR:LDAP.
-	 *
-	 * Escapes the given VALUES according to RFC 2254 so that they can be safely used in LDAP filters.
-	 *
-	 * Any control characters with an ACII code < 32 as well as the characters with special meaning in
-	 * LDAP filters "*", "(", ")", and "\" (the backslash) are converted into the representation of a
-	 * backslash followed by two hex digits representing the hexadecimal value of the character.
-	 *
-	 * @static
-	 * @param array $values Array of values to escape
-	 * @return array Array $values, but escaped
-	 */
-	public static function escape_filter_value($values = array(), $singleValue = TRUE) {
-		// Parameter validation
-		if (!is_array($values)) {
-			$values = array($values);
-		}
-
-		foreach ($values as $key => $val) {
-			// Escaping of filter meta characters
-			$val = str_replace('\\', '\5c', $val);
-			$val = str_replace('*',  '\2a', $val);
-			$val = str_replace('(',  '\28', $val);
-			$val = str_replace(')',  '\29', $val);
-
-			// ASCII < 32 escaping
-			$val = self::asc2hex32($val);
-
-			if (null === $val) $val = '\0';  // apply escaped "null" if string is empty
-
-			$values[$key] = $val;
-		}
-		if ($singleValue) return $values[0];
-		return $values;
-	}
-
-
-	/**
-	 * Borrowed function from PEAR:LDAP.
-	 *
-	 * Converts all ASCII chars < 32 to "\HEX"
-	 *
-	 * @param string $string String to convert
-	 *
-	 * @static
-	 * @return string
-	 */
-	public static function asc2hex32($string) {
-		for ($i = 0; $i < strlen($string); $i++) {
-			$char = substr($string, $i, 1);
-			if (ord($char) < 32) {
-				$hex = dechex(ord($char));
-				if (strlen($hex) == 1) $hex = '0'.$hex;
-				$string = str_replace($char, '\\'.$hex, $string);
-			}
-		}
-		return $string;
-	}
-
-	/**
-	 * Convert SASL authz_id into a DN
-	 */
-	private  function authzid_to_dn($searchBase, $searchAttributes, $authz_id) {
-		if (preg_match("/^dn:/", $authz_id))
-			return preg_replace("/^dn:/", "", $authz_id);
-
-		if (preg_match("/^u:/", $authz_id))
-			return $this->searchfordn($searchBase, $searchAttributes,
-			                          preg_replace("/^u:/", "", $authz_id));
-
-		return $authz_id;
-	}
-
-	/**
-	 * ldap_exop_whoami accessor, if available. Use requested authz_id
-	 * otherwise.
-	 *
-	 * ldap_exop_whoami is not yet included in PHP. For reference, the
-	 * feature request: http://bugs.php.net/bug.php?id=42060
-	 * And the patch against lastest PHP release:
-	 * http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/databases/php-ldap/files/ldap-ctrl-exop.patch
-	 */
-	public  function whoami($searchBase, $searchAttributes) {
-		$authz_id = '';
-
-		if (function_exists('ldap_exop_whoami')) {
-			if (ldap_exop_whoami($this->ldap, $authz_id) !== true)
-			    throw $this->getLDAPException('LDAP whoami exop failure');
-		} else {
-			$authz_id = $this->authz_id;
-		}
-
-		$dn = $this->authzid_to_dn($searchBase, $searchAttributes, $authz_id);
-
-		if (!isset($dn) || ($dn == ''))
-			throw $this->getLDAPException('Cannot figure userID');
-
-		return $dn;
-	}
+        // Remove the count and return
+        unset($results['count']);
+        return $results;
+    }
+
+
+    /**
+     * Bind to LDAP with a specific DN and password. Simple wrapper around
+     * ldap_bind() with some additional logging.
+     *
+     * @param string $dn
+     * The DN used.
+     * @param string $password
+     * The password used.
+     * @param array $sasl_args
+     * Array of SASL options for SASL bind
+     * @return bool
+     * Returns TRUE if successful, FALSE if
+     * LDAP_INVALID_CREDENTIALS, LDAP_X_PROXY_AUTHZ_FAILURE,
+     * LDAP_INAPPROPRIATE_AUTH, LDAP_INSUFFICIENT_ACCESS
+     * @throws SimpleSAML_Error_Exception on other errors
+     */
+    public function bind($dn, $password, array $sasl_args = NULL) {
+        $authz_id = null;
+
+        if ($sasl_args != NULL) {
+            if (!function_exists('ldap_sasl_bind')) {
+                $ex_msg = 'Library - missing SASL support';
+                throw $this->makeException($ex_msg);
+            }
+
+            // SASL Bind, with error handling.
+            $authz_id = $sasl_args['authz_id'];
+            $error = @ldap_sasl_bind($this->ldap, $dn, $password,
+                $sasl_args['mech'],
+                $sasl_args['realm'],
+                $sasl_args['authc_id'],
+                $sasl_args['authz_id'],
+                $sasl_args['props']);
+        } else {
+            // Simple Bind, with error handling.
+            $authz_id = $dn;
+            $error = @ldap_bind($this->ldap, $dn, $password);
+        }
+
+        if ($error === TRUE) {
+            // Good.
+            $this->authz_id = $authz_id;
+            SimpleSAML_Logger::debug('Library - LDAP bind(): Bind successful with DN \'' . $dn . '\'');
+            return TRUE;
+        }
+
+        /* Handle errors
+         * LDAP_INVALID_CREDENTIALS
+         * LDAP_INSUFFICIENT_ACCESS */
+        switch(ldap_errno($this->ldap)) {
+            case 32:	/* LDAP_NO_SUCH_OBJECT */
+                // no break
+            case 47:	/* LDAP_X_PROXY_AUTHZ_FAILURE */
+                // no break
+            case 48:	/* LDAP_INAPPROPRIATE_AUTH */
+                // no break
+            case 49:	/* LDAP_INVALID_CREDENTIALS */
+                // no break
+            case 50:	/* LDAP_INSUFFICIENT_ACCESS */
+                return FALSE;
+                break;
+        default;
+            break;
+        }
+
+        // Bad.
+        throw $this->makeException('Library - LDAP bind(): Bind failed with DN \'' . $dn . '\'');
+    }
+
+
+    /**
+     * Applies an LDAP option to the current connection.
+     *
+     * @throws Exception
+     * @param $option
+     * @param $value
+     * @return void
+     */
+    public function setOption($option, $value) {
+
+        // Attempt to set the LDAP option
+        if (!@ldap_set_option($this->ldap, $option, $value)) {
+            throw $this->makeException(
+	        'ldap:LdapConnection->setOption : Failed to set LDAP option [' .
+                $option . '] with the value [' . $value . '] error: ' . ldap_error($this->ldap),
+                ERR_INTERNAL
+            );
+        }
+
+        // Log debug message
+        SimpleSAML_Logger::debug(
+            'ldap:LdapConnection->setOption : Set the LDAP option [' .
+            $option . '] with the value [' . $value . ']'
+        );
+    }
+
+
+    /**
+     * Search a given DN for attributes, and return the resulting associative
+     * array.
+     *
+     * @param string $dn
+     * The DN of an element.
+     * @param string|array $attributes
+     * The names of the attribute(s) to retrieve. Defaults to NULL; that is,
+     * all available attributes. Note that this is not very effective.
+     * @param int $maxsize
+     * The maximum size of any attribute's value(s). If exceeded, the attribute
+     * will not be returned.
+     * @return array
+     * The array of attributes and their values.
+     * @see http://no.php.net/manual/en/function.ldap-read.php
+     */
+    public function getAttributes($dn, $attributes = NULL, $maxsize = NULL) {
+
+        // Preparations, including a pretty debug message...
+        $description = 'all attributes';
+        if (is_array($attributes)) {
+            $description = '\'' . join(',', $attributes) . '\'';
+        } else {
+            // Get all attributes...
+            // TODO: Verify that this originally was the intended behaviour. Could $attributes be a string?
+            $attributes = array();
+        }
+        SimpleSAML_Logger::debug('Library - LDAP getAttributes(): Getting ' . $description . ' from DN \'' . $dn . '\'');
+
+        // Attempt to get attributes.
+        // TODO: Should aliases be dereferenced?
+        $result = @ldap_read($this->ldap, $dn, 'objectClass=*', $attributes, 0, 0, $this->timeout);
+        if ($result === false) {
+            throw $this->makeException('Library - LDAP getAttributes(): Failed to get attributes from DN \'' . $dn . '\'');
+        }
+        $entry = @ldap_first_entry($this->ldap, $result);
+        if ($entry === false) {
+            throw $this->makeException('Library - LDAP getAttributes(): Could not get first entry from DN \'' . $dn . '\'');
+        }
+        $attributes = @ldap_get_attributes($this->ldap, $entry);  // Recycling $attributes... Possibly bad practice.
+        if ($attributes === false) {
+            throw $this->makeException('Library - LDAP getAttributes(): Could not get attributes of first entry from DN \'' . $dn . '\'');
+        }
+
+        // Parsing each found attribute into our result set.
+        $result = array();  // Recycling $result... Possibly bad practice.
+        for ($i = 0; $i < $attributes['count']; $i++) {
+
+            // Ignore attributes that exceed the maximum allowed size.
+            $name = $attributes[$i];
+            $attribute = $attributes[$name];
+
+            // Deciding whether to base64 encode.
+            $values = array();
+            for ($j = 0; $j < $attribute['count']; $j++) {
+                $value = $attribute[$j];
+
+                if (!empty($maxsize) && strlen($value) >= $maxsize) {
+                    // Ignoring and warning.
+                    SimpleSAML_Logger::warning('Library - LDAP getAttributes(): Attribute \'' .
+                        $name . '\' exceeded maximum allowed size by ' + ($maxsize - strlen($value)));
+                    continue;
+                }
+
+                // Base64 encode binary attributes.
+                if (strtolower($name) === 'jpegphoto' || strtolower($name) === 'objectguid') {
+                    $values[] = base64_encode($value);
+                } else {
+                    $values[] = $value;
+                }
+
+            }
+
+            // Adding.
+            $result[$name] = $values;
+
+        }
+
+        // We're done.
+        SimpleSAML_Logger::debug('Library - LDAP getAttributes(): Found attributes \'(' . join(',', array_keys($result)) . ')\'');
+        return $result;
+    }
+
+
+    /**
+     * Enter description here...
+     *
+     * @param string $config
+     * @param string $username
+     * @param string $password
+     * @return array|bool
+     */
+    // TODO: Documentation; only cleared up exception/log messages.
+    public function validate($config, $username, $password = null) {
+
+        /* Escape any characters with a special meaning in LDAP. The following
+         * characters have a special meaning (according to RFC 2253):
+         * ',', '+', '"', '\', '<', '>', ';', '*'
+         * These characters are escaped by prefixing them with '\'.
+         */
+        $username = addcslashes($username, ',+"\\<>;*');
+        $password = addcslashes($password, ',+"\\<>;*');
+
+        if (isset($config['priv_user_dn'])) {
+            $this->bind($config['priv_user_dn'], $config['priv_user_pw']);
+        }
+        if (isset($config['dnpattern'])) {
+            $dn = str_replace('%username%', $username, $config['dnpattern']);
+        } else {
+            $dn = $this->searchfordn($config['searchbase'], $config['searchattributes'], $username);
+        }
+
+        if ($password !== null) { /* checking users credentials ... assuming below that she may read her own attributes ... */
+            if (!$this->bind($dn, $password)) {
+                SimpleSAML_Logger::info('Library - LDAP validate(): Failed to authenticate \''. $username . '\' using DN \'' . $dn . '\'');
+                return FALSE;
+            }
+        }
+
+        /*
+         * Retrieve attributes from LDAP
+         */
+        $attributes = $this->getAttributes($dn, $config['attributes']);
+        return $attributes;
+
+    }
+
+
+    /**
+     * Borrowed function from PEAR:LDAP.
+     *
+     * Escapes the given VALUES according to RFC 2254 so that they can be safely used in LDAP filters.
+     *
+     * Any control characters with an ACII code < 32 as well as the characters with special meaning in
+     * LDAP filters "*", "(", ")", and "\" (the backslash) are converted into the representation of a
+     * backslash followed by two hex digits representing the hexadecimal value of the character.
+     *
+     * @static
+     * @param array $values Array of values to escape
+     * @return array Array $values, but escaped
+     */
+    public static function escape_filter_value($values = array(), $singleValue = TRUE) {
+        // Parameter validation
+        if (!is_array($values)) {
+            $values = array($values);
+        }
+
+        foreach ($values as $key => $val) {
+            // Escaping of filter meta characters
+            $val = str_replace('\\', '\5c', $val);
+            $val = str_replace('*',  '\2a', $val);
+            $val = str_replace('(',  '\28', $val);
+            $val = str_replace(')',  '\29', $val);
+
+            // ASCII < 32 escaping
+            $val = self::asc2hex32($val);
+
+            if (null === $val) {
+                $val = '\0';  // apply escaped "null" if string is empty
+            }
+
+            $values[$key] = $val;
+        }
+        if ($singleValue) {
+            return $values[0];
+        }
+        return $values;
+    }
+
+
+    /**
+     * Borrowed function from PEAR:LDAP.
+     *
+     * Converts all ASCII chars < 32 to "\HEX"
+     *
+     * @param string $string String to convert
+     *
+     * @static
+     * @return string
+     */
+    public static function asc2hex32($string) {
+        for ($i = 0; $i < strlen($string); $i++) {
+            $char = substr($string, $i, 1);
+            if (ord($char) < 32) {
+                $hex = dechex(ord($char));
+                if (strlen($hex) == 1) {
+                    $hex = '0'.$hex;
+                }
+                $string = str_replace($char, '\\'.$hex, $string);
+            }
+        }
+        return $string;
+    }
+
+    /**
+     * Convert SASL authz_id into a DN
+     */
+    private  function authzid_to_dn($searchBase, $searchAttributes, $authz_id) {
+        if (preg_match("/^dn:/", $authz_id)) {
+            return preg_replace("/^dn:/", "", $authz_id);
+        }
+
+        if (preg_match("/^u:/", $authz_id)) {
+            return $this->searchfordn($searchBase, $searchAttributes,
+                preg_replace("/^u:/", "", $authz_id));
+        }
+        return $authz_id;
+    }
+
+    /**
+     * ldap_exop_whoami accessor, if available. Use requested authz_id
+     * otherwise.
+     *
+     * ldap_exop_whoami is not yet included in PHP. For reference, the
+     * feature request: http://bugs.php.net/bug.php?id=42060
+     * And the patch against lastest PHP release:
+     * http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/databases/php-ldap/files/ldap-ctrl-exop.patch
+     */
+    public  function whoami($searchBase, $searchAttributes) {
+        $authz_id = '';
+
+        if (function_exists('ldap_exop_whoami')) {
+            if (ldap_exop_whoami($this->ldap, $authz_id) !== true) {
+                throw $this->getLDAPException('LDAP whoami exop failure');
+            }
+        } else {
+            $authz_id = $this->authz_id;
+        }
+
+        $dn = $this->authzid_to_dn($searchBase, $searchAttributes, $authz_id);
+
+        if (!isset($dn) || ($dn == '')) {
+            throw $this->getLDAPException('Cannot figure userID');
+        }
+
+        return $dn;
+    }
 
 }