From e78e6c29515ecc25cf17ed27d2c801e41acaa038 Mon Sep 17 00:00:00 2001 From: Jan Schneider Date: Tue, 14 Sep 2010 18:21:20 +0200 Subject: [PATCH] Cleanup --- framework/Ldap/lib/Horde/Ldap.php | 1621 +++++++++++++++++-------------------- 1 file changed, 724 insertions(+), 897 deletions(-) diff --git a/framework/Ldap/lib/Horde/Ldap.php b/framework/Ldap/lib/Horde/Ldap.php index 747200908..050576ce5 100644 --- a/framework/Ldap/lib/Horde/Ldap.php +++ b/framework/Ldap/lib/Horde/Ldap.php @@ -1,98 +1,70 @@ * @author Jan Wagner * @author Del * @author Benedikt Hallinger * @author Ben Klang * @author Chuck Hagenbuch - * @copyright 2009 The Horde Project + * @author Jan Schneider + * @copyright 2009-2010 The Horde Project * @copyright 2003-2007 Tarjej Huse, Jan Wagner, Del Elson, Benedikt Hallinger * @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 */ -/** - * Package includes. - */ -#require_once 'Net/Ldap/RootDSE.php'; -#require_once 'Net/Ldap/Schema.php'; -#require_once 'Net/Ldap/Entry.php'; -#require_once 'Net/Ldap/Search.php'; -#require_once 'Net/Ldap/Util.php'; -#require_once 'Net/Ldap/Filter.php'; -#require_once 'Net/Ldap/LDIF.php'; -#require_once 'Net/Ldap/SchemaCache.interface.php'; -#require_once 'Net/Ldap/SimpleFileSchemaCache.php'; - class Horde_Ldap { /** - * Error constants for errors that are not LDAP errors. - */ - const _ERROR = 1000; - - /** - * Library Version - */ - const VERSION = '0.1.0'; - - /** * Class configuration array * - * hostspec = the ldap host to connect to - * (may be an array of several hosts to try) - * port = the server port - * version = ldap version (defaults to v 3) - * starttls = when set, ldap_start_tls() is run after connecting. - * searchpw = password to use when searching LDAP - * seachdn = the DN to bind as when searching - * basedn = ldap base - * options = hash of ldap options to set (opt => val) - * filter = default search filter - * scope = default search scope - * - * Newly added in 2.0.0RC4, for auto-reconnect: - * auto_reconnect = if set to true then the class will automatically - * attempt to reconnect to the LDAP server in certain - * failure conditionswhen attempting a search, or other - * LDAP operation. Defaults to false. Note that if you - * set this to true, calls to search() may block - * indefinitely if there is a catastrophic server failure. - * min_backoff = minimum reconnection delay period (in seconds). - * current_backoff = initial reconnection delay period (in seconds). - * max_backoff = maximum reconnection delay period (in seconds). + * - hostspec: the LDAP host to connect to (may be an array of + * several hosts to try). + * - port: the server port. + * - version: LDAP version (defaults to 3). + * - starttls: when set, ldap_start_tls() is run after connecting. + * - searchpw: password to use when searching LDAP. + * - seachdn: the DN to bind as when searching. + * - basedn: LDAP base. + * - options: hash of LDAP options to set. + * - filter: default search filter. + * - scope: default search scope. + * - auto_reconnect: if true, the class will automatically + * - attempt to reconnect to the LDAP server in certain + * - failure conditions when attempting a search, or other + * - LDAP operations. Defaults to false. Note that if you + * - set this to true, calls to search() may block + * - indefinitely if there is a catastrophic server failure. + * - min_backoff: minimum reconnection delay period (in seconds). + * - current_backof: initial reconnection delay period (in seconds). + * - max_backoff: maximum reconnection delay period (in seconds). * - * @access protected * @var array */ - protected $_config = array('hostspec' => 'localhost', - 'port' => 389, - 'version' => 3, - 'starttls' => false, - 'searchdn' => '', - 'searchpw' => '', - 'writeas' => 'search', - 'writedn' => '', - 'writepw' => '', - 'basedn' => '', - 'options' => array(), - 'filter' => '(objectClass=*)', - 'scope' => 'sub', - 'auto_reconnect' => false, - 'min_backoff' => 1, - 'current_backoff' => 1, - 'max_backoff' => 32); + protected $_config = array( + 'hostspec' => 'localhost', + 'port' => 389, + 'version' => 3, + 'starttls' => false, + 'searchdn' => '', + 'searchpw' => '', + 'writeas' => 'search', + 'writedn' => '', + 'writepw' => '', + 'basedn' => '', + 'options' => array(), + 'filter' => '(objectClass=*)', + 'scope' => 'sub', + 'auto_reconnect' => false, + 'min_backoff' => 1, + 'current_backoff' => 1, + 'max_backoff' => 32); /** - * List of hosts we try to establish a connection to + * List of hosts we try to establish a connection to. * - * @access protected * @var array */ protected $_host_list = array(); @@ -100,7 +72,6 @@ class Horde_Ldap /** * List of hosts that are known to be down. * - * @access protected * @var array */ protected $_down_host_list = array(); @@ -108,33 +79,29 @@ class Horde_Ldap /** * LDAP resource link. * - * @access protected * @var resource */ - protected $_link = false; + protected $_link; /** - * Horde_Ldap_Schema object - * - * This gets set and returned by {@link schema()} + * Schema object. * - * @access protected - * @var object Horde_Ldap_Schema + * @see schema() + * @var Horde_Ldap_Schema */ - protected $_schema = null; + protected $_schema; /** - * Schema cacher function callback + * Schema cache function callback. * * @see registerSchemaCache() * @var string */ - protected $_schema_cache = null; + protected $_schema_cache; /** - * Cache for attribute encoding checks + * Cache for attribute encoding checks. * - * @access protected * @var array Hash with attribute names as key and boolean value * to determine whether they should be utf8 encoded or not. */ @@ -143,72 +110,53 @@ class Horde_Ldap /** * Cache for rootDSE objects * - * Hash with requested rootDSE attr names as key and rootDSE object as value + * Hash with requested rootDSE attr names as key and rootDSE + * object as value. * * Since the RootDSE object itself may request a rootDSE object, * {@link rootDse()} caches successful requests. * Internally, Horde_Ldap needs several lookups to this object, so * caching increases performance significally. * - * @access protected * @var array */ protected $_rootDSE_cache = array(); /** - * Returns the Horde_Ldap Release version, may be called statically + * Configures Horde_Ldap, connects and binds. * - * @static - * @return string Horde_Ldap version - */ - public static function getVersion() - { - return Horde_Ldap::VERSION; - } - - /** - * Configure Horde_Ldap, connect and bind + * Use this method as starting point of using Horde_Ldap to + * establish a connection to your LDAP server. * - * Use this method as starting point of using Horde_Ldap - * to establish a connection to your LDAP server. + * Static function that returns either an error object or the new + * Horde_Ldap object. Something like a factory. Takes a config + * array with the needed parameters. * - * Static function that returns either an error object or the new Horde_Ldap - * object. Something like a factory. Takes a config array with the needed - * parameters. + * @todo better error handling for setConfig()? * * @param array $config Configuration array * - * @access public * @return Horde_Ldap Horde_Ldap object + * @throws Horde_Ldap_Exception */ - public static function &connect($config = array()) + public static function connect($config = array()) { - $ldap_check = self::checkLDAPExtension(); - + self::checkLDAPExtension(); $obj = new Horde_Ldap($config); - - // todo? better errorhandling for setConfig()? - - // connect and bind with credentials in config $obj->bind(); - return $obj; } /** - * Horde_Ldap constructor - * - * Sets the config array + * Constructor. * - * Please note that the usual way of getting Horde_Ldap to work is - * to call something like: + * The usual way of getting Horde_Ldap to work is to call + * something like: * $ldap = Horde_Ldap::connect($ldap_config); * - * @param array $config Configuration array - * - * @access protected - * @return void * @see $_config + * + * @param array $config Configuration array. */ public function __construct($config = array()) { @@ -216,206 +164,183 @@ class Horde_Ldap } /** - * Sets the internal configuration array - * - * @param array $config Configuration array + * Destructor. + */ + public function __destruct() + { + $this->disconnect(); + } + + /** + * Sets the internal configuration array. * - * @access protected - * @return void + * @param array $config Configuration hash. */ protected function setConfig($config) { - // - // Parameter check -- probably should raise an error here if config - // is not an array. - // - if (! is_array($config)) { + /* Parameter check -- probably should raise an error here if + * config is not an array. */ + if (!is_array($config)) { return; } foreach ($config as $k => $v) { if (isset($this->_config[$k])) { $this->_config[$k] = $v; - } else { - // map old (Net_Ldap) parms to new ones - switch($k) { - case "dn": - $this->_config["searchdn"] = $v; - break; - case "password": - $this->_config["searchpw"] = $v; - break; - case "tls": - $this->_config["starttls"] = $v; - break; - case "base": - $this->_config["basedn"] = $v; - break; - case "host": - $this->_config["hostspec"] = $v; - break; - } } } - // - // Ensure the host list is an array. - // + /* Ensure the host list is an array. */ if (is_array($this->_config['hostspec'])) { $this->_host_list = $this->_config['hostspec']; } else { - if (strlen($this->_config['hostspec']) > 0) { + if (strlen($this->_config['hostspec'])) { $this->_host_list = array($this->_config['hostspec']); } else { $this->_host_list = array(); - // ^ this will cause an error in performConnect(), - // so the user is notified about the failure + /* This will cause an error in performConnect(), so + * the user is notified about the failure. */ } } - // - // Reset the down host list, which seems like a sensible thing to do - // if the config is being reset for some reason. - // + /* Reset the down host list, which seems like a sensible thing + * to do if the config is being reset for some reason. */ $this->_down_host_list = array(); } /** - * Bind or rebind to the ldap-server + * Bind or rebind to the LDAP server. * - * This function binds with the given dn and password to the server. In case - * no connection has been made yet, it will be started and startTLS issued - * if appropiate. + * This function binds with the given DN and password to the + * server. In case no connection has been made yet, it will be + * started and STARTTLS issued if appropiate. * - * The internal bind configuration is not being updated, so if you call - * bind() without parameters, you can rebind with the credentials - * provided at first connecting to the server. + * The internal bind configuration is not being updated, so if you + * call bind() without parameters, you can rebind with the + * credentials provided at first connecting to the server. * - * @param string $dn Distinguished name for binding - * @param string $password Password for binding + * @param string $dn DN for binding. + * @param string $password Password for binding. * - * @access public - * @return true true on success + * @throws Horde_Ldap_Exception */ public function bind($dn = null, $password = null) { - // fetch current bind credentials + /* Fetch current bind credentials. */ if (empty($dn)) { - $dn = $this->_config["searchdn"]; + $dn = $this->_config['searchdn']; } if (empty($password)) { - $password = $this->_config["searchpw"]; + $password = $this->_config['searchpw']; } - // Connect first, if we haven't so far. - // This will also bind us to the server. - if ($this->_link === false) { - // store old credentials so we can revert them later - // then overwrite config with new bind credentials - $olddn = $this->_config["searchdn"]; - $oldpw = $this->_config["searchpw"]; + /* Connect first, if we haven't so far. This will also bind + * us to the server. */ + if (!$this->_link) { + /* Store old credentials so we can revert them later, then + * overwrite config with new bind credentials. */ + $olddn = $this->_config['searchdn']; + $oldpw = $this->_config['searchpw']; - // overwrite bind credentials in config - // so performConnect() knows about them - $this->_config["searchdn"] = $dn; - $this->_config["searchpw"] = $password; + /* Overwrite bind credentials in config so + * performConnect() knows about them. */ + $this->_config['searchdn'] = $dn; + $this->_config['searchpw'] = $password; - // try to connect with provided credentials + /* Try to connect with provided credentials. */ $msg = $this->performConnect(); - // reset to previous config - $this->_config["searchdn"] = $olddn; - $this->_config["searchpw"] = $oldpw; + /* Reset to previous config. */ + $this->_config['searchdn'] = $olddn; + $this->_config['searchpw'] = $oldpw; + return; + } + + /* Do the requested bind as we are asked to bind manually. */ + if (is_null($dn)) { + /* Anonymous bind. */ + $msg = @ldap_bind($this->_link); } else { - // do the requested bind as we are - // asked to bind manually - if (is_null($dn)) { - // anonymous bind - $msg = @ldap_bind($this->_link); - } else { - // privileged bind - $msg = @ldap_bind($this->_link, $dn, $password); - } - if (false === $msg) { - throw new Horde_Ldap_Exception("Bind failed: " . - @ldap_error($this->_link), - @ldap_errno($this->_link)); - } + /* Privileged bind. */ + $msg = @ldap_bind($this->_link, $dn, $password); + } + if (!$msg) { + throw new Horde_Ldap_Exception('Bind failed: ' . + @ldap_error($this->_link), + @ldap_errno($this->_link)); } - return true; } /** - * Connect to the LDAP server + * Connects to the LDAP server. * - * This function connects to the LDAP server specified in - * the configuration, binds and set up the LDAP protocol as needed. + * This function connects to the LDAP server specified in the + * configuration, binds and set up the LDAP protocol as needed. * - * @access protected - * @return true true on success + * @throws Horde_Ldap_Exception */ protected function performConnect() { - // Note: Connecting is briefly described in RFC1777. - // Basicly it works like this: - // 1. set up TCP connection - // 2. secure that connection if neccessary - // 3a. setLDAPVersion to tell server which version we want to speak - // 3b. perform bind - // 3c. setLDAPVersion to tell server which version we want to speak - // together with a test for supported versions - // 4. set additional protocol options - - // Return true if we are already connected. - if ($this->_link !== false) { - return true; + /* Connecting is briefly described in RFC1777. Basicly it works like + * this: + * 1. set up TCP connection + * 2. secure that connection if neccessary + * 3a. setLDAPVersion to tell server which version we want to speak + * 3b. perform bind + * 3c. setLDAPVersion to tell server which version we want to speak + * together with a test for supported versions + * 4. set additional protocol options */ + + /* Return if we are already connected. */ + if ($this->_link) { + return; } - // Connnect to the LDAP server if we are not connected. Note that - // with some LDAP clients, ldapperformConnect returns a link value even - // if no connection is made. We need to do at least one anonymous - // bind to ensure that a connection is actually valid. - // - // Ref: http://www.php.net/manual/en/function.ldap-connect.php + /* Connnect to the LDAP server if we are not connected. Note that + * ldap_connect() may return a link value even if no connection is + * made. We need to do at least one anonymous bind to ensure that a + * connection is actually valid. + * + * See: http://www.php.net/manual/en/function.ldap-connect.php */ - // Default error message in case all connection attempts - // fail but no message is set + /* Default error message in case all connection attempts fail but no + * message is set. */ $current_error = new Horde_Ldap_Exception('Unknown connection error'); - // Catch empty $_host_list arrays. - if (!is_array($this->_host_list) || count($this->_host_list) == 0) { + /* Catch empty $_host_list arrays. */ + if (!is_array($this->_host_list) || !count($this->_host_list)) { throw new Horde_Ldap_Exception('No servers configured'); } - // Cycle through the host list. + /* Cycle through the host list. */ foreach ($this->_host_list as $host) { - - // Ensure we have a valid string for host name + /* Ensure we have a valid string for host name. */ if (is_array($host)) { $current_error = new Horde_Ldap_Exception('No Servers configured'); continue; } - // Skip this host if it is known to be down. + /* Skip this host if it is known to be down. */ if (in_array($host, $this->_down_host_list)) { continue; } - // Record the host that we are actually connecting to in case - // we need it later. + /* Record the host that we are actually connecting to in case we + * need it later. */ $this->_config['hostspec'] = $host; - // Attempt a connection. + /* Attempt a connection. */ $this->_link = @ldap_connect($host, $this->_config['port']); - if (false === $this->_link) { + if (!$this->_link) { $current_error = new Horde_Ldap_Exception('Could not connect to ' . $host . ':' . $this->_config['port']); $this->_down_host_list[] = $host; continue; } - // If we're supposed to use TLS, do so before we try to bind, - // as some strict servers only allow binding via secure connections - if ($this->_config["starttls"] === true) { + /* If we're supposed to use TLS, do so before we try to bind, as + * some strict servers only allow binding via secure + * connections. */ + if ($this->_config['starttls']) { try { $this->startTLS(); } catch (Horde_Ldap_Exception $e) { @@ -426,39 +351,40 @@ class Horde_Ldap } } - // Try to set the configured LDAP version on the connection if LDAP - // server needs that before binding (eg OpenLDAP). - // This could be necessary since rfc-1777 states that the protocol - // version has to be set at the bind request. - // We use force here which means that the test in the rootDSE is - // skipped; this is neccessary, because some strict LDAP servers - // only allow to // read the LDAP rootDSE (which tells us the - // supported protocol versions) with authenticated clients. - // This may fail in which case we try again after binding. - // In this case, most probably the bind() or setLDAPVersion()-call - // below will also fail, providing error messages. + /* Try to set the configured LDAP version on the connection if LDAP + * server needs that before binding (eg OpenLDAP). + * This could be necessary since RFC 1777 states that the protocol + * version has to be set at the bind request. + * We use force here which means that the test in the rootDSE is + * skipped; this is neccessary, because some strict LDAP servers + * only allow to read the LDAP rootDSE (which tells us the + * supported protocol versions) with authenticated clients. + * This may fail in which case we try again after binding. + * In this case, most probably the bind() or setLDAPVersion() call + * below will also fail, providing error messages. */ $version_set = false; $this->setLDAPVersion(0, true); - // Attempt to bind to the server. If we have credentials configured, - // we try to use them, otherwise its an anonymous bind. - // As stated by RFC-1777, the bind request should be the first - // operation to be performed after the connection is established. - // This may give an protocol error if the server does not support - // V2 binds and the above call to setLDAPVersion() failed. - // In case the above call failed, we try an V2 bind here and set the - // version afterwards (with checking to the rootDSE). + /* Attempt to bind to the server. If we have credentials + * configured, we try to use them, otherwise it's an anonymous + * bind. + * As stated by RFC 1777, the bind request should be the first + * operation to be performed after the connection is established. + * This may give an protocol error if the server does not support + * v2 binds and the above call to setLDAPVersion() failed. + * If the above call failed, we try an v2 bind here and set the + * version afterwards (with checking to the rootDSE). */ try { $msg = $this->bind(); } catch (Exception $e) { - // The bind failed, discard link and save error msg. - // Then record the host as down and try next one + /* The bind failed, discard link and save error msg. + * Then record the host as down and try next one. */ if ($e->getCode() == 0x02 && !$version_set) { - // provide a finer grained error message - // if protocol error arises because of invalid version - $e = new Horde_Ldap_Exception($e->getMessage(). - " (could not set LDAP protocol version to ". - $this->_config['version'].")", + /* Provide a finer grained error message if protocol error + * arises because of invalid version. */ + $e = new Horde_Ldap_Exception($e->getMessage() + . ' (could not set LDAP protocol version to ' + . $this->_config['version'].')', $e->getCode()); } $this->_link = false; @@ -467,12 +393,12 @@ class Horde_Ldap continue; } - // Set desired LDAP version if not successfully set before. - // Here, a check against the rootDSE is performed, so we get a - // error message if the server does not support the version. - // The rootDSE entry should tell us which LDAP versions are - // supported. However, some strict LDAP servers only allow - // bound suers to read the rootDSE. + /* Set desired LDAP version if not successfully set before. + * Here, a check against the rootDSE is performed, so we get a + * error message if the server does not support the version. + * The rootDSE entry should tell us which LDAP versions are + * supported. However, some strict LDAP servers only allow + * bound users to read the rootDSE. */ if (!$version_set) { try { $this->setLDAPVersion(); @@ -484,7 +410,8 @@ class Horde_Ldap } } - // Set LDAP parameters, now we know we have a valid connection. + /* Set LDAP parameters, now that we know we have a valid + * connection. */ if (isset($this->_config['options']) && is_array($this->_config['options']) && count($this->_config['options'])) { @@ -500,318 +427,256 @@ class Horde_Ldap } } - // At this stage we have connected, bound, and set up options, - // so we have a known good LDAP server. Time to go home. - return true; + /* At this stage we have connected, bound, and set up options, so + * we have a known good LDAP server. Time to go home. */ + return; } - - // All connection attempts have failed, return the last error. + /* All connection attempts have failed, return the last error. */ throw $current_error; } /** - * Reconnect to the LDAP server. - * - * In case the connection to the LDAP - * service has dropped out for some reason, this function will reconnect, - * and re-bind if a bind has been attempted in the past. It is probably - * most useful when the server list provided to the new() or connect() - * function is an array rather than a single host name, because in that - * case it will be able to connect to a failover or secondary server in - * case the primary server goes down. - * - * This doesn't return anything, it just tries to re-establish - * the current connection. It will sleep for the current backoff - * period (seconds) before attempting the connect, and if the - * connection fails it will double the backoff period, but not - * try again. If you want to ensure a reconnection during a - * transient period of server downtime then you need to call this - * function in a loop. - * - * @access protected - * @return true true on success + * Reconnects to the LDAP server. + * + * In case the connection to the LDAP service has dropped out for some + * reason, this function will reconnect, and re-bind if a bind has been + * attempted in the past. It is probably most useful when the server list + * provided to the new() or connect() function is an array rather than a + * single host name, because in that case it will be able to connect to a + * failover or secondary server in case the primary server goes down. + * + * This method just tries to re-establish the current connection. It will + * sleep for the current backoff period (seconds) before attempting the + * connect, and if the connection fails it will double the backoff period, + * but not try again. If you want to ensure a reconnection during a + * transient period of server downtime then you need to call this function + * in a loop. + * + * @throws Horde_Ldap_Exception */ protected function performReconnect() { - - // Return true if we are already connected. - if ($this->_link !== false) { - return true; + /* Return if we are already connected. */ + if ($this->_link) { + return; } - // Default error message in case all connection attempts - // fail but no message is set - $current_error = new Horde_Ldap_Exception('Unknown connection error'); - - // Sleep for a backoff period in seconds. + /* Sleep for a backoff period in seconds. */ sleep($this->_config['current_backoff']); - // Retry all available connections. + /* Retry all available connections. */ $this->_down_host_list = array(); try { $this->performConnect(); } catch (Horde_Ldap_Exception $e) { - $this->_config['current_backoff'] = - $this->_config['current_backoff'] * 2; + $this->_config['current_backoff'] *= 2; if ($this->_config['current_backoff'] > $this->_config['max_backoff']) { $this->_config['current_backoff'] = $this->_config['max_backoff']; } throw $e; } - // Now we should be able to safely (re-)bind. + /* Now we should be able to safely (re-)bind. */ try { $this->bind(); } catch (Exception $e) { - $this->_config['current_backoff'] = $this->_config['current_backoff'] * 2; + $this->_config['current_backoff'] *= 2; if ($this->_config['current_backoff'] > $this->_config['max_backoff']) { $this->_config['current_backoff'] = $this->_config['max_backoff']; } - // _config['hostspec'] should have had the last connected host stored in it - // by performConnect(). Since we are unable to bind to that host we can safely - // assume that it is down or has some other problem. + /* $this->_config['hostspec'] should have had the last connected + * host stored in it by performConnect(). Since we are unable to + * bind to that host we can safely assume that it is down or has + * some other problem. */ $this->_down_host_list[] = $this->_config['hostspec']; - throw e; + throw $e; } - // At this stage we have connected, bound, and set up options, - // so we have a known good LDAP server. Time to go home. + /* At this stage we have connected, bound, and set up options, so we + * have a known good LDAP server. Time to go home. */ $this->_config['current_backoff'] = $this->_config['min_backoff']; - return true; } /** - * Starts an encrypted session + * Starts an encrypted session. * - * @access public - * @return true true on success + * @throws Horde_Ldap_Exception */ public function startTLS() { - // Test to see if the server supports TLS first. - // This is done via testing the extensions offered by the server. - // The OID 1.3.6.1.4.1.1466.20037 tells whether TLS is supported. + /* Test to see if the server supports TLS first. + * This is done via testing the extensions offered by the server. + * The OID 1.3.6.1.4.1.1466.20037 tells whether TLS is supported. */ try { $rootDSE = $this->rootDse(); } catch (Exception $e) { - throw new Horde_Ldap_Exception("Unable to fetch rootDSE entry ". - "to see if TLS is supoported: ".$e->getMessage(), $e->getCode()); + throw new Horde_Ldap_Exception('Unable to fetch rootDSE entry to see if TLS is supoported: ' . $e->getMessage(), $e->getCode()); } try { $supported_extensions = $rootDSE->getValue('supportedExtension'); } catch (Exception $e) { - throw new Horde_Ldap_Exception("Unable to fetch rootDSE attribute 'supportedExtension' ". - "to see if TLS is supoported: ".$e->getMessage(), $e->getCode()); + throw new Horde_Ldap_Exception('Unable to fetch rootDSE attribute "supportedExtension" to see if TLS is supoported: ' . $e->getMessage(), $e->getCode()); } - if (in_array('1.3.6.1.4.1.1466.20037', $supported_extensions)) { - if (false === @ldap_start_tls($this->_link)) { - throw new Horde_Ldap_Exception("TLS not started: " . - @ldap_error($this->_link), - @ldap_errno($this->_link)); - } - return true; - } else { - throw new Horde_Ldap_Exception("Server reports that it does not support TLS"); + if (!in_array('1.3.6.1.4.1.1466.20037', $supported_extensions)) { + throw new Horde_Ldap_Exception('Server reports that it does not support TLS'); } - } - /** - * Close LDAP connection. - * - * Closes the connection. Use this when the session is over. - * - * @return void - */ - public function done() - { - $this->_Horde_Ldap(); + if (!@ldap_start_tls($this->_link)) { + throw new Horde_Ldap_Exception('TLS not started: ' . @ldap_error($this->_link)); + } } /** - * Alias for {@link done()} - * - * @return void - * @see done() + * Closes the LDAP connection. */ public function disconnect() { - $this->done(); - } - - /** - * Destructor - * - * @access protected - */ - private function _Horde_Ldap() - { @ldap_close($this->_link); } /** - * Add a new entryobject to a directory. + * Adds a new entry to the directory. + * + * This also links the entry to the connection used for the add, if it was + * a fresh entry. * - * Use add to add a new Horde_Ldap_Entry object to the directory. - * This also links the entry to the connection used for the add, - * if it was a fresh entry ({@link HordeLdap_Entry::createFresh()}) + * @see HordeLdap_Entry::createFresh() * - * @param Horde_Ldap_Entry &$entry HordeLdap_Entry + * @param Horde_Ldap_Entry $entry An LDAP entry. * - * @return true Horde_Ldap_Error object or true + * @throws Horde_Ldap_Exception */ - public function add(Horde_Ldap_Entry &$entry) + public function add(Horde_Ldap_Entry $entry) { - // Rebind as the write DN + /* Rebind as the write DN. */ if (!empty($this->writedn)) { $this->bind($this->writedn, $this->writepw); } - // Continue attempting the add operation in a loop until we - // get a success, a definitive failure, or the world ends. + /* Continue attempting the add operation in a loop until we get a + * success, a definitive failure, or the world ends. */ while (true) { $link = $this->getLink(); - if ($link === false) { - // We do not have a successful connection yet. The call to - // getLink() would have kept trying if we wanted one. Go - // home now. - throw new Horde_Ldap_Exception("Could not add entry " . $entry->dn() . - " no valid LDAP connection could be found."); + /* We do not have a successful connection yet. The call to + * getLink() would have kept trying if we wanted one. */ + throw new Horde_Ldap_Exception('Could not add entry ' . $entry->dn() . ' no valid LDAP connection could be found.'); } if (@ldap_add($link, $entry->dn(), $entry->getValues())) { - // entry successfully added, we should update its $ldap reference - // in case it is not set so far (fresh entry) - if (!$entry->getLDAP() instanceof Horde_Ldap) { + /* Entry successfully added, we should update its Horde_Ldap + * reference in case it is not set so far (fresh entry). */ + if (!($entry->getLDAP() instanceof Horde_Ldap)) { $entry->setLDAP($this); } - // store, that the entry is present inside the directory + /* Store that the entry is present inside the directory. */ $entry->markAsNew(false); - return true; - } else { - // We have a failure. What type? We may be able to reconnect - // and try again. - $error_code = @ldap_errno($link); - $error_name = $this->errorMessage($error_code); + return; + } - if (($error_name === 'LDAP_OPERATIONS_ERROR') && - ($this->_config['auto_reconnect'])) { + /* We have a failure. What kind? We may be able to reconnect and + * try again. */ + $error_code = @ldap_errno($link); + $error_name = $this->errorMessage($error_code); - // The server has become disconnected before trying the - // operation. We should try again, possibly with a different - // server. - $this->_link = false; - $this->performReconnect(); - } else { - // Errors other than the above catched are just passed - // back to the user so he may react upon them. - throw new Horde_Ldap_Exception("Could not add entry " . $entry->dn() . " " . - $error_name, - $error_code); - } + if ($error_name != 'LDAP_OPERATIONS_ERROR' | + !$this->_config['auto_reconnect']) { + /* Errors other than the above are just passed back to the user + * so he may react upon them. */ + throw new Horde_Ldap_Exception('Could not add entry ' . $entry->dn() . ' ' . $error_name, $error_code); } + + /* The server has disconnected before trying the operation. We + * should try again, possibly with a different server. */ + $this->_link = false; + $this->performReconnect(); } } /** - * Delete an entry from the directory + * Deletes an entry from the directory. * - * The object may either be a string representing the dn or a Horde_Ldap_Entry - * object. When the boolean paramter recursive is set, all subentries of the - * entry will be deleted as well. - * - * @param string|Horde_Ldap_Entry $dn DN-string or Horde_Ldap_Entry - * @param boolean $recursive Should we delete all children recursive as well? - * - * @access public - * @return true true on success + * @param string|Horde_Ldap_Entry $dn DN string or Horde_Ldap_Entry. + * @param boolean $recursive Should we delete all children + * recursivelx as well? + * @throws Horde_Ldap_Exception */ public function delete($dn, $recursive = false) { if ($dn instanceof Horde_Ldap_Entry) { $dn = $dn->dn(); } - if (false === is_string($dn)) { - throw new Horde_Ldap_Exception("Parameter is not a string nor an entry object!"); + if (!is_string($dn)) { + throw new Horde_Ldap_Exception('Parameter is not a string nor an entry object!'); } - // Re-bind as the write DN if not using searchdn credentials + /* Re-bind as the write DN if not using searchdn credentials. */ if (!empty($this->writedn)) { $this->bind($this->writedn, $this->writepw); } - // Recursive delete searches for children and calls delete for them + /* Recursive delete searches for children and calls delete for them. */ if ($recursive) { $result = @ldap_list($this->_link, $dn, '(objectClass=*)', array(null), 0, 0); if (@ldap_count_entries($this->_link, $result)) { - $subentry = @ldap_first_entry($this->_link, $result); - $this->delete(@ldap_get_dn($this->_link, $subentry), true); - while ($subentry = @ldap_next_entry($this->_link, $subentry)) { + for ($subentry = @ldap_first_entry($this->_link, $result); + $subentry = @ldap_next_entry($this->_link, $subentry);) { $this->delete(@ldap_get_dn($this->_link, $subentry), true); } } } - // Continue attempting the delete operation in a loop until we - // get a success, a definitive failure, or the world ends. + /* Continue the delete operation in a loop until we get a success, or a + * definitive failure. */ while (true) { $link = $this->getLink(); - - if ($link === false) { - // We do not have a successful connection yet. The call to - // getLink() would have kept trying if we wanted one. Go - // home now. - throw new Horde_Ldap_Exception("Could not add entry " . $dn . - " no valid LDAP connection could be found."); + if (!$link) { + /* We do not have a successful connection yet. The call to + * getLink() would have kept trying if we wanted one. */ + throw new Horde_Ldap_Exception('Could not add entry ' . $dn . ' no valid LDAP connection could be found.'); } if (@ldap_delete($link, $dn)) { - // entry successfully deleted. - return true; - } else { - // We have a failure. What type? - // We may be able to reconnect and try again. - $error_code = @ldap_errno($link); - $error_name = $this->errorMessage($error_code); - - if (($this->errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') && - ($this->_config['auto_reconnect'])) { - // The server has become disconnected before trying the - // operation. We should try again, possibly with a - // different server. - $this->_link = false; - $this->performReconnect(); - - } elseif ($error_code == 66) { - // Subentries present, server refused to delete. - // Deleting subentries is the clients responsibility, but - // since the user may not know of the subentries, we do not - // force that here but instead notify the developer so he - // may take actions himself. - throw new Horde_Ldap_Exception("Could not delete entry $dn because of subentries. Use the recursive parameter to delete them."); + /* Entry successfully deleted. */ + return; + } - } else { - // Errors other than the above catched are just passed - // back to the user so he may react upon them. - throw new Horde_Ldap_Exception("Could not delete entry " . $dn . " " . - $error_name, - $error_code); - } + /* We have a failure. What kind? We may be able to reconnect and + * try again. */ + $error_code = @ldap_errno($link); + $error_name = $this->errorMessage($error_code); + if ($this->errorMessage($error_code) == 'LDAP_OPERATIONS_ERROR' && + $this->_config['auto_reconnect']) { + /* The server has disconnected before trying the operation. We + * should try again, possibly with a different server. */ + $this->_link = false; + $this->performReconnect(); + } elseif ($error_code == 66) { + /* Subentries present, server refused to delete. + * Deleting subentries is the clients responsibility, but since + * the user may not know of the subentries, we do not force + * that here but instead notify the developer so he may take + * actions himself. */ + throw new Horde_Ldap_Exception('Could not delete entry ' . $dn . ' because of subentries. Use the recursive parameter to delete them.'); + } else { + /* Errors other than the above catched are just passed back to + * the user so he may react upon them. */ + throw new Horde_Ldap_Exception('Could not delete entry ' . $dn . ' ' . $error_name, $error_code); } } } /** - * Modify an ldapentry directly on the server - * - * This one takes the DN or a Horde_Ldap_Entry object and an array of actions. - * This array should be something like this: + * Modifies an LDAP entry on the server. * + * The $params argument is an array of actions and should be something like + * this: + * * array('add' => array('attribute1' => array('val1', 'val2'), * 'attribute2' => array('val1')), * 'delete' => array('attribute1'), @@ -819,15 +684,13 @@ class Horde_Ldap * 'changes' => array('add' => ..., * 'replace' => ..., * 'delete' => array('attribute1', 'attribute2' => array('val1'))) + * * - * The changes array is there so the order of operations can be influenced - * (the operations are done in order of appearance). * The order of execution is as following: * 1. adds from 'add' array * 2. deletes from 'delete' array * 3. replaces from 'replace' array * 4. changes (add, replace, delete) in order of appearance - * All subarrays (add, replace, delete, changes) may be given at the same time. * * The function calls the corresponding functions of an Horde_Ldap_Entry * object. A detailed description of array structures can be found there. @@ -836,15 +699,18 @@ class Horde_Ldap * this method will instantly carry out an update() after each operation, * thus modifying "directly" on the server. * - * @param string|Horde_Ldap_Entry $entry DN-string or Horde_Ldap_Entry - * @param array $parms Array of changes + * @see Horde_Ldap_Entry::add() + * @see Horde_Ldap_Entry::delete() + * @see Horde_Ldap_Entry::replace() * - * @access public - * @return true true on success + * @param string|Horde_Ldap_Entry $entry DN string or Horde_Ldap_Entry. + * @param array $parms Array of changes + * + * @throws Horde_Ldap_Exception */ public function modify($entry, $parms = array()) { - // Re-bind as the write DN + /* Re-bind as the write DN. */ if (!empty($this->writedn)) { $this->bind($this->writedn, $this->writepw); } @@ -852,129 +718,87 @@ class Horde_Ldap if (is_string($entry)) { $entry = $this->getEntry($entry); } - if (!$entry instanceof Horde_Ldap_Entry) { - throw new Horde_Ldap_Exception("Parameter is not a string nor an entry object!"); + if (!($entry instanceof Horde_Ldap_Entry)) { + throw new Horde_Ldap_Exception('Parameter is not a string nor an entry object!'); } - // Perform changes mentioned separately + /* Perform changes mentioned separately. */ foreach (array('add', 'delete', 'replace') as $action) { - if (isset($parms[$action])) { - $msg = $entry->$action($parms[$action]); - - $entry->setLDAP($this); - - // Because the @ldap functions are called inside Horde_Ldap_Entry::update(), - // we have to trap the error codes issued from that if we want to support - // reconnection. - while (true) { - try { - $entry->update(); - break; - } catch (Exception $e) { - // We have a failure. What type? We may be able to reconnect - // and try again. - $error_code = $e->getCode(); - $error_name = $this->errorMessage($error_code); - - if (($this->errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') && - ($this->_config['auto_reconnect'])) { - - // The server has become disconnected before trying the - // operation. We should try again, possibly with a different - // server. - $this->_link = false; - $this->performReconnect(); - - } else { + if (!isset($parms[$action])) { + continue; + } + $entry->$action($parms[$action]); + $entry->setLDAP($this); - // Errors other than the above catched are just passed - // back to the user so he may react upon them. - throw new Horde_Ldap_Exception("Could not modify entry: ".$e->getMessage()); - } + /* Because the ldap_*() functions are called inside + * Horde_Ldap_Entry::update(), we have to trap the error codes + * issued from that if we want to support reconnection. */ + while (true) { + try { + $entry->update(); + break; + } catch (Exception $e) { + /* We have a failure. What kind? We may be able to + * reconnect and try again. */ + $error_code = $e->getCode(); + $error_name = $this->errorMessage($error_code); + + if ($this->errorMessage($error_code) != 'LDAP_OPERATIONS_ERROR' || + !$this->_config['auto_reconnect']) { + /* Errors other than the above catched are just passed + * back to the user so he may react upon them. */ + throw new Horde_Ldap_Exception('Could not modify entry: ' . $e->getMessage()); } + /* The server has disconnected before trying the operation. + * We should try again, possibly with a different + * server. */ + $this->_link = false; + $this->performReconnect(); } } } - // perform combined changes in 'changes' array - if (isset($parms['changes']) && is_array($parms['changes'])) { - foreach ($parms['changes'] as $action => $value) { - - // Because the @ldap functions are called inside Horde_Ldap_Entry::update, - // we have to trap the error codes issued from that if we want to support - // reconnection. - while (true) { - try { - $this->modify($entry, array($action => $value)); - break; - } catch (Exception $e) { - // We have a failure. What type? We may be able to reconnect - // and try again. - $error_code = $e->getCode(); - $error_name = $this->errorMessage($error_code); - - if (($this->errorMessage($error_code) === 'LDAP_OPERATIONS_ERROR') && - ($this->_config['auto_reconnect'])) { - - // The server has become disconnected before trying the - // operation. We should try again, possibly with a different - // server. - $this->_link = false; - $this->performReconnect(); - - } else { - // Errors other than the above catched are just passed - // back to the user so he may react upon them. - return $msg; - } - } - } - } + if (!isset($parms['changes']) || !is_array($parms['changes'])) { + return; } - return true; + /* Perform combined changes in 'changes' array. */ + foreach ($parms['changes'] as $action => $value) { + $this->modify($entry, array($action => $value)); + } } /** - * Run a ldap search query - * - * Search is used to query the ldap-database. - * $base and $filter may be ommitted. The one from config will - * then be used. $base is either a DN-string or an Horde_Ldap_Entry - * object in which case its DN willb e used. - * - * Params may contain: - * - * scope: The scope which will be used for searching - * base - Just one entry - * sub - The whole tree - * one - Immediately below $base - * sizelimit: Limit the number of entries returned (default: 0 = unlimited), - * timelimit: Limit the time spent for searching (default: 0 = unlimited), - * attrsonly: If true, the search will only return the attribute names, - * attributes: Array of attribute names, which the entry should contain. - * It is good practice to limit this to just the ones you need. - * [NOT IMPLEMENTED] - * deref: By default aliases are dereferenced to locate the base object for the search, but not when - * searching subordinates of the base object. This may be changed by specifying one of the - * following values: - * - * never - Do not dereference aliases in searching or in locating the base object of the search. - * search - Dereference aliases in subordinates of the base object in searching, but not in - * locating the base object of the search. - * find - * always - * - * Please note, that you cannot override server side limitations to sizelimit - * and timelimit: You can always only lower a given limit. - * - * @param string|Horde_Ldap_Entry $base LDAP searchbase - * @param string|Horde_Ldap_Filter $filter LDAP search filter or a Horde_Ldap_Filter object - * @param array $params Array of options + * Runs an LDAP search query. + * + * $base and $filter may be ommitted. The one from config will then be + * used. $base is either a DN-string or an Horde_Ldap_Entry object in which + * case its DN will be used. + * + * $params may contain: + * - scope: The scope which will be used for searching: + * - base: Just one entry + * - sub: The whole tree + * - one: Immediately below $base + * - sizelimit: Limit the number of entries returned + * (default: 0 = unlimited) + * - timelimit: Limit the time spent for searching (default: 0 = unlimited) + * - attrsonly: If true, the search will only return the attribute names + * - attributes: Array of attribute names, which the entry should contain. + * It is good practice to limit this to just the ones you + * need. + * + * You cannot override server side limitations to sizelimit and timelimit: + * You can always only lower a given limit. * - * @access public - * @return Horde_Ldap_Search Horde_Ldap_Search object * @todo implement search controls (sorting etc) + * + * @param string|Horde_Ldap_Entry $base LDAP searchbase. + * @param string|Horde_Ldap_Filter $filter LDAP search filter. + * @param array $params Array of options. + * + * @return Horde_Ldap_Search The search result. + * @throws Horde_Ldap_Exception */ public function search($base = null, $filter = null, $params = array()) { @@ -982,33 +806,37 @@ class Horde_Ldap $base = $this->_config['basedn']; } if ($base instanceof Horde_Ldap_Entry) { - $base = $base->dn(); // fetch DN of entry, making searchbase relative to the entry + /* Fetch DN of entry, making searchbase relative to the entry. */ + $base = $base->dn(); } if (is_null($filter)) { $filter = $this->_config['filter']; } if ($filter instanceof Horde_Ldap_Filter) { - $filter = $filter->asString(); // convert Horde_Ldap_Filter to string representation + /* Convert Horde_Ldap_Filter to string representation. */ + $filter = $filter->asString(); } - /* setting searchparameters */ - (isset($params['sizelimit'])) ? $sizelimit = $params['sizelimit'] : $sizelimit = 0; - (isset($params['timelimit'])) ? $timelimit = $params['timelimit'] : $timelimit = 0; - (isset($params['attrsonly'])) ? $attrsonly = $params['attrsonly'] : $attrsonly = 0; - (isset($params['attributes'])) ? $attributes = $params['attributes'] : $attributes = array(); + /* Setting search parameters. */ + $sizelimit = isset($params['sizelimit']) ? $params['sizelimit'] : 0; + $timelimit = isset($params['timelimit']) ? $params['timelimit'] : 0; + $attrsonly = isset($params['attrsonly']) ? $params['attrsonly'] : 0; + $attributes = isset($params['attributes']) ? $params['attributes'] : array(); - // Ensure $attributes to be an array in case only one - // attribute name was given as string + /* Ensure $attributes to be an array in case only one attribute name + * was given as string. */ if (!is_array($attributes)) { $attributes = array($attributes); } - // reorganize the $attributes array index keys - // sometimes there are problems with not consecutive indexes + /* Reorganize the $attributes array index keys sometimes there are + * problems with not consecutive indexes. */ $attributes = array_values($attributes); - // scoping makes searches faster! - $scope = (isset($params['scope']) ? $params['scope'] : $this->_config['scope']); + /* Scoping makes searches faster! */ + $scope = isset($params['scope']) + ? $params['scope'] + : $this->_config['scope']; switch ($scope) { case 'one': @@ -1021,8 +849,8 @@ class Horde_Ldap $search_function = 'ldap_search'; } - // Continue attempting the search operation until we get a success - // or a definitive failure. + /* Continue attempting the search operation until we get a success or a + * definitive failure. */ while (true) { $link = $this->getLink(); $search = @call_user_func($search_function, @@ -1036,16 +864,20 @@ class Horde_Ldap if ($err = @ldap_errno($link)) { if ($err == 32) { - // Errorcode 32 = no such object, i.e. a nullresult. - return $obj = new Horde_Ldap_Search ($search, $this, $attributes); - } elseif ($err == 4) { - // Errorcode 4 = sizelimit exeeded. - return $obj = new Horde_Ldap_Search ($search, $this, $attributes); - } elseif ($err == 87) { - // bad search filter - throw new Horde_Ldap_Exception($this->errorMessage($err) . "($filter)", $err); - } elseif (($err == 1) && ($this->_config['auto_reconnect'])) { - // Errorcode 1 = LDAP_OPERATIONS_ERROR but we can try a reconnect. + /* Errorcode 32 = no such object, i.e. a nullresult. */ + return new Horde_Ldap_Search ($search, $this, $attributes); + } + if ($err == 4) { + /* Errorcode 4 = sizelimit exeeded. */ + return new Horde_Ldap_Search ($search, $this, $attributes); + } + if ($err == 87) { + /* Bad search filter. */ + throw new Horde_Ldap_Exception($this->errorMessage($err) . '($filter)', $err); + } + if (($err == 1) && ($this->_config['auto_reconnect'])) { + /* Errorcode 1 = LDAP_OPERATIONS_ERROR but we can try a + * reconnect. */ $this->_link = false; $this->performReconnect(); } else { @@ -1053,88 +885,81 @@ class Horde_Ldap throw new Horde_Ldap_Exception($this->errorMessage($err) . $msg, $err); } } else { - return $obj = new Horde_Ldap_Search($search, $this, $attributes); + return new Horde_Ldap_Search($search, $this, $attributes); } } } /** - * Set an LDAP option + * Sets an LDAP option. * - * @param string $option Option to set - * @param mixed $value Value to set Option to + * @param string $option Option to set. + * @param mixed $value Value to set option to. * - * @access public - * @return true true on success + * @throws Horde_Ldap_Exception */ public function setOption($option, $value) { - if ($this->_link) { - if (defined($option)) { - if (@ldap_set_option($this->_link, constant($option), $value)) { - return true; - } else { - $err = @ldap_errno($this->_link); - if ($err) { - $msg = @ldap_err2str($err); - } else { - $err = Horde_Ldap::_ERROR; - $msg = $this->errorMessage($err); - } - throw new Horde_Ldap_Exception($msg, $err); - } - } else { - throw new Horde_Ldap_Exception("Unkown Option requested"); - } + if (!$this->_link) { + throw new Horde_Ldap_Exception('Could not set LDAP option: No LDAP connection'); + } + if (!defined($option)) { + throw new Horde_Ldap_Exception('Unkown option requested'); + } + if (@ldap_set_option($this->_link, constant($option), $value)) { + return; + } + $err = @ldap_errno($this->_link); + if ($err) { + $msg = @ldap_err2str($err); } else { - throw new Horde_Ldap_Exception("Could not set LDAP option: No LDAP connection"); + $err = 1000; + $msg = $this->errorMessage($err); } + throw new Horde_Ldap_Exception($msg, $err); } /** - * Get an LDAP option value + * Returns an LDAP option value. * - * @param string $option Option to get + * @param string $option Option to get. * - * @access public * @return Horde_Ldap_Error|string Horde_Ldap_Error or option value + * @throws Horde_Ldap_Exception */ 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 = Horde_Ldap::_ERROR; - $msg = $this->errorMessage($err); - } - throw new Horde_Ldap_Exception($msg, $err); - } - } else { - throw new Horde_Ldap_Exception("Unkown Option requested"); - } + 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) { + $msg = @ldap_err2str($err); } else { - throw new Horde_Ldap_Exception("No LDAP connection"); + $err = 1000; + $msg = $this->errorMessage($err); } + throw new Horde_Ldap_Exception($msg, $err); } /** - * Get the LDAP_PROTOCOL_VERSION that is used on the connection. + * Returns the LDAP protocol version that is used on the connection. * - * A lot of ldap functionality is defined by what protocol version the ldap server speaks. - * This might be 2 or 3. + * A lot of LDAP functionality is defined by what protocol version + * the LDAP server speaks. This might be 2 or 3. * - * @return int + * @return integer The protocol version. */ public function getLDAPVersion() { if ($this->_link) { - $version = $this->getOption("LDAP_OPT_PROTOCOL_VERSION"); + $version = $this->getOption('LDAP_OPT_PROTOCOL_VERSION'); } else { $version = $this->_config['version']; } @@ -1142,13 +967,16 @@ class Horde_Ldap } /** - * Set the LDAP_PROTOCOL_VERSION that is used on the connection. + * Sets the LDAP protocol version that is used on the connection. + * + * @todo Checking via the rootDSE takes much time - why? fetching + * and instanciation is quick! * - * @param int $version LDAP-version that should be used - * @param boolean $force If set to true, the check against the rootDSE will be skipped + * @param integer $version LDAP version that should be used. + * @param boolean $force If set to true, the check against the rootDSE + * will be skipped. * - * @return true true - * @todo Checking via the rootDSE takes much time - why? fetching and instanciation is quick! + * @throws Horde_Ldap_Exception */ public function setLDAPVersion($version = 0, $force = false) { @@ -1156,14 +984,12 @@ class Horde_Ldap $version = $this->_config['version']; } - // - // Check to see if the server supports this version first. - // - // Todo: Why is this so horribly slow? - // $this->rootDse() is very fast, as well as Horde_Ldap_RootDSE::fetch() - // seems like a problem at copiyng the object inside PHP?? - // Additionally, this is not always reproducable... - // + /* Check to see if the server supports this version first. + * + * TODO: Why is this so horribly slow? $this->rootDse() is + * very fast, as well as Horde_Ldap_RootDSE::fetch() seems + * like a problem at copiyng the object inside PHP?? + * Additionally, this is not always reproducable... */ if (!$force) { $rootDSE = $this->rootDse(); $supported_versions = $rootDSE->getValue('supportedLDAPVersion'); @@ -1174,36 +1000,37 @@ class Horde_Ldap } if ($force || $check_ok) { - return $this->setOption("LDAP_OPT_PROTOCOL_VERSION", $version); - } else { - throw new Horde_Ldap_Exception("LDAP Server does not support protocol version " . $version); + return $this->setOption('LDAP_OPT_PROTOCOL_VERSION', $version); } + throw new Horde_Ldap_Exception('LDAP Server does not support protocol version ' . $version); } /** - * Tells if a DN does exist in the directory + * Returns whether a DN exists in the directory. * - * @param string|Horde_Ldap_Entry $dn The DN of the object to test + * @param string|Horde_Ldap_Entry $dn The DN of the object to test. * - * @return boolean + * @return boolean True if the DN exists. + * @throws Horde_Ldap_Exception */ public function dnExists($dn) { if ($dn instanceof Horde_Ldap_Entry) { $dn = $dn->dn(); } - if (false === is_string($dn)) { + if (!is_string($dn)) { throw new Horde_Ldap_Exception('Parameter $dn is not a string nor an entry object!'); } - // make dn relative to parent + /* Make dn relative to parent. */ $base = Horde_Ldap_Util::ldap_explode_dn($dn, array('casefold' => 'none', 'reverse' => false, 'onlyvalues' => false)); $entry_rdn = array_shift($base); if (is_array($entry_rdn)) { - // maybe the dn consist of a multivalued RDN, we must build the dn in this case - // because the $entry_rdn is an array! + /* Maybe the dn consist of a multivalued RDN. We must + * build the dn in this case because the $entry_rdn is an + * array. */ $filter_dn = Horde_Ldap_Util::canonical_dn($entry_rdn); } $base = Horde_Ldap_Util::canonical_dn($base); @@ -1223,267 +1050,256 @@ class Horde_Ldap /** - * Get a specific entry based on the DN + * Returns a specific entry based on the DN. * - * @param string $dn DN of the entry that should be fetched - * @param array $attr Array of Attributes to select. If ommitted, all attributes are fetched. + * @todo Maybe a check against the schema should be done to be + * sure the attribute type exists. * - * @return Horde_Ldap_Entry Reference to a Horde_Ldap_Entry object - * @todo Maybe check against the shema should be done to be sure the attribute type exists + * @param string $dn DN of the entry that should be fetched. + * @param array $attr Array of Attributes to select. If ommitted, all + * attributes are fetched. + * + * @return Horde_Ldap_Entry A Horde_Ldap_Entry object. + * @throws Horde_Ldap_Exception */ - public function &getEntry($dn, $attr = array()) + public function getEntry($dn, $attr = array()) { if (!is_array($attr)) { $attr = array($attr); } $result = $this->search($dn, '(objectClass=*)', array('scope' => 'base', 'attributes' => $attr)); - if ($result->count() == 0) { + if (!$result->count()) { throw new Horde_Ldap_Exception('Could not fetch entry '.$dn.': no entry found'); } $entry = $result->shiftEntry(); - if (false == $entry) { + if (!$entry) { throw new Horde_Ldap_Exception('Could not fetch entry (error retrieving entry from search result)'); } return $entry; } /** - * Rename or move an entry + * Renames or moves an entry. + * + * This method will instantly carry out an update() after the + * move, so the entry is moved instantly. * - * This method will instantly carry out an update() after the move, - * so the entry is moved instantly. - * You can pass an optional Horde_Ldap object. In this case, a cross directory - * move will be performed which deletes the entry in the source (THIS) directory - * and adds it in the directory $target_ldap. - * A cross directory move will switch the Entrys internal LDAP reference so - * updates to the entry will go to the new directory. + * You can pass an optional Horde_Ldap object. In this case, a + * cross directory move will be performed which deletes the entry + * in the source (THIS) directory and adds it in the directory + * $target_ldap. * - * Note that if you want to do a cross directory move, you need to - * pass an Horde_Ldap_Entry object, otherwise the attributes will be empty. + * A cross directory move will switch the entry's internal LDAP + * reference so updates to the entry will go to the new directory. * - * @param string|Horde_Ldap_Entry $entry Entry DN or Entry object - * @param string $newdn New location - * @param Horde_Ldap $target_ldap (optional) Target directory for cross server move; should be passed via reference + * If you want to do a cross directory move, you need to pass an + * Horde_Ldap_Entry object, otherwise the attributes will be + * empty. * - * @return true + * @param string|Horde_Ldap_Entry $entry An LDAP entry. + * @param string $newdn The new location. + * @param Horde_Ldap $target_ldap Target directory for cross + * server move. + * + * @throws Horde_Ldap_Exception */ public function move($entry, $newdn, $target_ldap = null) { if (is_string($entry)) { - $entry_o = $this->getEntry($entry); - } else { - $entry_o =& $entry; + if ($target_ldap && $target_ldap !== $this) { + throw new Horde_Ldap_Exception('Unable to perform cross directory move: operation requires a Horde_Ldap_Entry object'); + } + $entry = $this->getEntry($entry); } - if (!$entry_o instanceof Horde_Ldap_Entry) { + if (!$entry instanceof Horde_Ldap_Entry) { throw new Horde_Ldap_Exception('Parameter $entry is expected to be a Horde_Ldap_Entry object! (If DN was passed, conversion failed)'); } - if (null !== $target_ldap && !$target_ldap instanceof Horde_Ldap) { + if ($target_ldap && !($target_ldap instanceof Horde_Ldap)) { throw new Horde_Ldap_Exception('Parameter $target_ldap is expected to be a Horde_Ldap object!'); } - if ($target_ldap && $target_ldap !== $this) { - // cross directory move - if (is_string($entry)) { - throw new Horde_Ldap_Exception('Unable to perform cross directory move: operation requires a Horde_Ldap_Entry object'); - } - if ($target_ldap->dnExists($newdn)) { - throw new Horde_Ldap_Exception('Unable to perform cross directory move: entry does exist in target directory'); - } - $entry_o->dn($newdn); - try { - $target_ldap->add($entry_o); - } catch (Exception $e) { - throw new Horde_Ldap_Exception('Unable to perform cross directory move: '.$e->getMessage().' in target directory'); - } + if (!$target_ldap || $target_ldap === $this) { + /* Local move. */ + $entry->dn($newdn); + $entry->setLDAP($this); + $entry->update(); + return; + } + /* Cross directory move. */ + if ($target_ldap->dnExists($newdn)) { + throw new Horde_Ldap_Exception('Unable to perform cross directory move: entry does exist in target directory'); + } + $entry->dn($newdn); + try { + $target_ldap->add($entry); + } catch (Exception $e) { + throw new Horde_Ldap_Exception('Unable to perform cross directory move: ' . $e->getMessage() . ' in target directory'); + } + + try { + $this->delete($entry->currentDN()); + } catch (Exception $e) { try { - $this->delete($entry_o->currentDN()); + $add_error_string = ''; + /* Undo add. */ + $target_ldap->delete($entry); } catch (Exception $e) { - try { - $add_error_string = ''; - $target_ldap->delete($entry_o); // undo add - } catch (Exception $e) { - $add_error_string = 'Additionally, the deletion (undo add) of $entry in target directory failed.'; - } - throw new Horde_Ldap_Exception('Unable to perform cross directory move: '.$e->getMessage().' in source directory. '.$add_error_string); + $add_error_string = ' Additionally, the deletion (undo add) of $entry in target directory failed.'; } - $entry_o->setLDAP($target_ldap); - return true; - } else { - // local move - $entry_o->dn($newdn); - $entry_o->setLDAP($this); - return $entry_o->update(); + throw new Horde_Ldap_Exception('Unable to perform cross directory move: ' . $e->getMessage() . ' in source directory.' . $add_error_string); } + $entry->setLDAP($target_ldap); } /** - * Copy an entry to a new location + * Copies an entry to a new location. * - * The entry will be immediately copied. - * Please note that only attributes you have + * The entry will be immediately copied. Only attributes you have * selected will be copied. * - * @param Horde_Ldap_Entry &$entry Entry object - * @param string $newdn New FQF-DN of the entry + * @param Horde_Ldap_Entry $entry An LDAP entry. + * @param string $newdn New FQF-DN of the entry. * - * @return Horde_Ldap_Entry Error Message or reference to the copied entry + * @return Horde_Ldap_Entry The copied entry. + * @throws Horde_Ldap_Exception */ - public function ©(&$entry, $newdn) + public function copy($entry, $newdn) { if (!$entry instanceof Horde_Ldap_Entry) { throw new Horde_Ldap_Exception('Parameter $entry is expected to be a Horde_Ldap_Entry object'); } $newentry = Horde_Ldap_Entry::createFresh($newdn, $entry->getValues()); - $result = $this->add($newentry); + $this->add($newentry); return $newentry; } /** - * Returns the string for an ldap errorcode. + * Returns the string for an LDAP errorcode. + * + * Made to be able to make better errorhandling. Function based + * on DB::errorMessage(). * - * Made to be able to make better errorhandling - * Function based on DB::errorMessage() - * Tip: The best description of the errorcodes is found here: + * Hint: The best description of the errorcodes is found here: * http://www.directory-info.com/Ldap/LDAPErrorCodes.html * - * @param int $errorcode Error code + * @param integer $errorcode An error code. * - * @return string The errorstring for the error. + * @return string The description for the error. */ public function errorMessage($errorcode) { $errorMessages = array( - 0x00 => "LDAP_SUCCESS", - 0x01 => "LDAP_OPERATIONS_ERROR", - 0x02 => "LDAP_PROTOCOL_ERROR", - 0x03 => "LDAP_TIMELIMIT_EXCEEDED", - 0x04 => "LDAP_SIZELIMIT_EXCEEDED", - 0x05 => "LDAP_COMPARE_FALSE", - 0x06 => "LDAP_COMPARE_TRUE", - 0x07 => "LDAP_AUTH_METHOD_NOT_SUPPORTED", - 0x08 => "LDAP_STRONG_AUTH_REQUIRED", - 0x09 => "LDAP_PARTIAL_RESULTS", - 0x0a => "LDAP_REFERRAL", - 0x0b => "LDAP_ADMINLIMIT_EXCEEDED", - 0x0c => "LDAP_UNAVAILABLE_CRITICAL_EXTENSION", - 0x0d => "LDAP_CONFIDENTIALITY_REQUIRED", - 0x0e => "LDAP_SASL_BIND_INPROGRESS", - 0x10 => "LDAP_NO_SUCH_ATTRIBUTE", - 0x11 => "LDAP_UNDEFINED_TYPE", - 0x12 => "LDAP_INAPPROPRIATE_MATCHING", - 0x13 => "LDAP_CONSTRAINT_VIOLATION", - 0x14 => "LDAP_TYPE_OR_VALUE_EXISTS", - 0x15 => "LDAP_INVALID_SYNTAX", - 0x20 => "LDAP_NO_SUCH_OBJECT", - 0x21 => "LDAP_ALIAS_PROBLEM", - 0x22 => "LDAP_INVALID_DN_SYNTAX", - 0x23 => "LDAP_IS_LEAF", - 0x24 => "LDAP_ALIAS_DEREF_PROBLEM", - 0x30 => "LDAP_INAPPROPRIATE_AUTH", - 0x31 => "LDAP_INVALID_CREDENTIALS", - 0x32 => "LDAP_INSUFFICIENT_ACCESS", - 0x33 => "LDAP_BUSY", - 0x34 => "LDAP_UNAVAILABLE", - 0x35 => "LDAP_UNWILLING_TO_PERFORM", - 0x36 => "LDAP_LOOP_DETECT", - 0x3C => "LDAP_SORT_CONTROL_MISSING", - 0x3D => "LDAP_INDEX_RANGE_ERROR", - 0x40 => "LDAP_NAMING_VIOLATION", - 0x41 => "LDAP_OBJECT_CLASS_VIOLATION", - 0x42 => "LDAP_NOT_ALLOWED_ON_NONLEAF", - 0x43 => "LDAP_NOT_ALLOWED_ON_RDN", - 0x44 => "LDAP_ALREADY_EXISTS", - 0x45 => "LDAP_NO_OBJECT_CLASS_MODS", - 0x46 => "LDAP_RESULTS_TOO_LARGE", - 0x47 => "LDAP_AFFECTS_MULTIPLE_DSAS", - 0x50 => "LDAP_OTHER", - 0x51 => "LDAP_SERVER_DOWN", - 0x52 => "LDAP_LOCAL_ERROR", - 0x53 => "LDAP_ENCODING_ERROR", - 0x54 => "LDAP_DECODING_ERROR", - 0x55 => "LDAP_TIMEOUT", - 0x56 => "LDAP_AUTH_UNKNOWN", - 0x57 => "LDAP_FILTER_ERROR", - 0x58 => "LDAP_USER_CANCELLED", - 0x59 => "LDAP_PARAM_ERROR", - 0x5a => "LDAP_NO_MEMORY", - 0x5b => "LDAP_CONNECT_ERROR", - 0x5c => "LDAP_NOT_SUPPORTED", - 0x5d => "LDAP_CONTROL_NOT_FOUND", - 0x5e => "LDAP_NO_RESULTS_RETURNED", - 0x5f => "LDAP_MORE_RESULTS_TO_RETURN", - 0x60 => "LDAP_CLIENT_LOOP", - 0x61 => "LDAP_REFERRAL_LIMIT_EXCEEDED", - 1000 => "Unknown Error" - ); + 0x00 => 'LDAP_SUCCESS', + 0x01 => 'LDAP_OPERATIONS_ERROR', + 0x02 => 'LDAP_PROTOCOL_ERROR', + 0x03 => 'LDAP_TIMELIMIT_EXCEEDED', + 0x04 => 'LDAP_SIZELIMIT_EXCEEDED', + 0x05 => 'LDAP_COMPARE_FALSE', + 0x06 => 'LDAP_COMPARE_TRUE', + 0x07 => 'LDAP_AUTH_METHOD_NOT_SUPPORTED', + 0x08 => 'LDAP_STRONG_AUTH_REQUIRED', + 0x09 => 'LDAP_PARTIAL_RESULTS', + 0x0a => 'LDAP_REFERRAL', + 0x0b => 'LDAP_ADMINLIMIT_EXCEEDED', + 0x0c => 'LDAP_UNAVAILABLE_CRITICAL_EXTENSION', + 0x0d => 'LDAP_CONFIDENTIALITY_REQUIRED', + 0x0e => 'LDAP_SASL_BIND_INPROGRESS', + 0x10 => 'LDAP_NO_SUCH_ATTRIBUTE', + 0x11 => 'LDAP_UNDEFINED_TYPE', + 0x12 => 'LDAP_INAPPROPRIATE_MATCHING', + 0x13 => 'LDAP_CONSTRAINT_VIOLATION', + 0x14 => 'LDAP_TYPE_OR_VALUE_EXISTS', + 0x15 => 'LDAP_INVALID_SYNTAX', + 0x20 => 'LDAP_NO_SUCH_OBJECT', + 0x21 => 'LDAP_ALIAS_PROBLEM', + 0x22 => 'LDAP_INVALID_DN_SYNTAX', + 0x23 => 'LDAP_IS_LEAF', + 0x24 => 'LDAP_ALIAS_DEREF_PROBLEM', + 0x30 => 'LDAP_INAPPROPRIATE_AUTH', + 0x31 => 'LDAP_INVALID_CREDENTIALS', + 0x32 => 'LDAP_INSUFFICIENT_ACCESS', + 0x33 => 'LDAP_BUSY', + 0x34 => 'LDAP_UNAVAILABLE', + 0x35 => 'LDAP_UNWILLING_TO_PERFORM', + 0x36 => 'LDAP_LOOP_DETECT', + 0x3C => 'LDAP_SORT_CONTROL_MISSING', + 0x3D => 'LDAP_INDEX_RANGE_ERROR', + 0x40 => 'LDAP_NAMING_VIOLATION', + 0x41 => 'LDAP_OBJECT_CLASS_VIOLATION', + 0x42 => 'LDAP_NOT_ALLOWED_ON_NONLEAF', + 0x43 => 'LDAP_NOT_ALLOWED_ON_RDN', + 0x44 => 'LDAP_ALREADY_EXISTS', + 0x45 => 'LDAP_NO_OBJECT_CLASS_MODS', + 0x46 => 'LDAP_RESULTS_TOO_LARGE', + 0x47 => 'LDAP_AFFECTS_MULTIPLE_DSAS', + 0x50 => 'LDAP_OTHER', + 0x51 => 'LDAP_SERVER_DOWN', + 0x52 => 'LDAP_LOCAL_ERROR', + 0x53 => 'LDAP_ENCODING_ERROR', + 0x54 => 'LDAP_DECODING_ERROR', + 0x55 => 'LDAP_TIMEOUT', + 0x56 => 'LDAP_AUTH_UNKNOWN', + 0x57 => 'LDAP_FILTER_ERROR', + 0x58 => 'LDAP_USER_CANCELLED', + 0x59 => 'LDAP_PARAM_ERROR', + 0x5a => 'LDAP_NO_MEMORY', + 0x5b => 'LDAP_CONNECT_ERROR', + 0x5c => 'LDAP_NOT_SUPPORTED', + 0x5d => 'LDAP_CONTROL_NOT_FOUND', + 0x5e => 'LDAP_NO_RESULTS_RETURNED', + 0x5f => 'LDAP_MORE_RESULTS_TO_RETURN', + 0x60 => 'LDAP_CLIENT_LOOP', + 0x61 => 'LDAP_REFERRAL_LIMIT_EXCEEDED', + 1000 => 'Unknown Error'); return isset($errorMessages[$errorcode]) ? $errorMessages[$errorcode] : - $errorMessages[Horde_Ldap::_ERROR] . ' (' . $errorcode . ')'; + 'Unknown Error (' . $errorcode . ')'; } /** - * Gets a rootDSE object + * Returns a rootDSE object * * This either fetches a fresh rootDSE object or returns it from * the internal cache for performance reasons, if possible. * - * @param array $attrs Array of attributes to search for + * @param array $attrs Array of attributes to search for. * - * @access public * @return Horde_Ldap_RootDSE Horde_Ldap_RootDSE object + * @throws Horde_Ldap_Exception */ - public function &rootDse($attrs = null) + public function rootDse(array $attrs = array()) { - if ($attrs !== null && !is_array($attrs)) { - throw new Horde_Ldap_Exception('Parameter $attr is expected to be an array'); - } - $attrs_signature = serialize($attrs); - // see if we need to fetch a fresh object, or if we already - // requested this object with the same attributes - if (true || !array_key_exists($attrs_signature, $this->_rootDSE_cache)) { - $rootdse =& Horde_Ldap_RootDSE::fetch($this, $attrs); - - // search was ok, store rootDSE in cache - $this->_rootDSE_cache[$attrs_signature] = $rootdse; + /* See if we need to fetch a fresh object, or if we already + * requested this object with the same attributes. */ + if (!isset($this->_rootDSE_cache[$attrs_signature])) { + $this->_rootDSE_cache[$attrs_signature] = Horde_Ldap_RootDSE::fetch($this, $attrs); } - return $this->_rootDSE_cache[$attrs_signature]; - } - /** - * Alias function of rootDse() for perl-ldap interface - * - * @access public - * @see rootDse() - * @return Horde_Ldap_RootDSE - */ - public function &root_dse() - { - $args = func_get_args(); - return call_user_func_array(array(&$this, 'rootDse'), $args); + return $this->_rootDSE_cache[$attrs_signature]; } /** - * Get a schema object + * Returns a schema object * - * @param string $dn (optional) Subschema entry dn + * @param string $dn Subschema entry dn. * - * @access public * @return Horde_Ldap_Schema Horde_Ldap_Schema object + * @throws Horde_Ldap_Exception */ - public function &schema($dn = null) + public function schema($dn = null) { - // If a schema caching object is registered, we use that to fetch - // a schema object. - // See registerSchemaCache() for more info on this. - // FIXME: Convert to Horde_Cache + /* If a schema caching object is registered, we use that to fetch + * a schema object. + * See registerSchemaCache() for more info on this. + * FIXME: Convert to Horde_Cache */ if ($this->_schema === null) { if ($this->_schema_cache) { $cached_schema = $this->_schema_cache->loadSchema(); @@ -1493,20 +1309,27 @@ class Horde_Ldap } } - // Fetch schema, if not tried before and no cached version available. - // If we are already fetching the schema, we will skip fetching. + /* Fetch schema, if not tried before and no cached version + * available. If we are already fetching the schema, we will + * skip fetching. */ if ($this->_schema === null) { - // store a temporary error message so subsequent calls to schema() can - // detect, that we are fetching the schema already. - // Otherwise we will get an infinite loop at Horde_Ldap_Schema::fetch() + /* Store a temporary error message so subsequent calls to + * schema() can detect that we are fetching the schema + * already. Otherwise we will get an infinite loop at + * Horde_Ldap_Schema::fetch(). */ $this->_schema = new Horde_Ldap_Exception('Schema not initialized'); $this->_schema = Horde_Ldap_Schema::fetch($this, $dn); - // If schema caching is active, advise the cache to store the schema + /* If schema caching is active, advise the cache to store + * the schema. */ if ($this->_schema_cache) { - $caching_result = $this->_schema_cache->storeSchema($this->_schema); + $this->_schema_cache->storeSchema($this->_schema); } } + + if ($this->_schema instanceof Horde_Ldap_Exception) { + throw $this->_schema; + } return $this->_schema; } @@ -1555,40 +1378,40 @@ class Horde_Ldap } } - /** - * Checks if PHP's LDAP extension is loaded + * Checks if PHP's LDAP extension is loaded. * * If it is not loaded, it tries to load it manually using PHP's dl(). * It knows both windows-dll and *nix-so. * - * @static - * @return true + * @throws Horde_Ldap_Exception */ public static function checkLDAPExtension() { if (!extension_loaded('ldap') && !@dl('ldap.' . PHP_SHLIB_SUFFIX)) { - throw new Horde_Ldap_Exception("Unable to locate PHP LDAP extension. Please install it before using the Horde_Ldap package."); + throw new Horde_Ldap_Exception('Unable to locate PHP LDAP extension. Please install it before using the Horde_Ldap package.'); } else { return true; } } /** - * Encodes given attributes to UTF8 if needed by schema + * @todo Remove this and expect all data to be UTF-8. + * + * Encodes given attributes to UTF8 if needed by schema. * - * This function takes attributes in an array and then checks against the schema if they need - * UTF8 encoding. If that is so, they will be encoded. An encoded array will be returned and - * can be used for adding or modifying. + * This function takes attributes in an array and then checks + * against the schema if they need UTF8 encoding. If that is the + * case, they will be encoded. An encoded array will be returned + * and can be used for adding or modifying. * * $attributes is expected to be an array with keys describing * the attribute names and the values as the value of this attribute: * $attributes = array('cn' => 'foo', 'attr2' => array('mv1', 'mv2')); * - * @param array $attributes Array of attributes + * @param array $attributes An array of attributes. * - * @access public - * @return array|Horde_Ldap_Error Array of UTF8 encoded attributes or Error + * @return array|Horde_Ldap_Error An array of UTF8 encoded attributes or an error. */ public function utf8Encode($attributes) { @@ -1596,6 +1419,8 @@ class Horde_Ldap } /** + * @todo Remove this and expect all data to be UTF-8. + * * Decodes the given attribute values if needed by schema * * $attributes is expected to be an array with keys describing @@ -1614,13 +1439,15 @@ class Horde_Ldap } /** + * @todo Remove this and expect all data to be UTF-8. + * * Encodes or decodes attribute values if needed * * @param array $attributes Array of attributes * @param array $function Function to apply to attribute values * * @access protected - * @return array|Horde_Ldap_Error Array of attributes with function applied to values or Error + * @return array Array of attributes with function applied to values. */ protected function utf8($attributes, $function) { @@ -1675,39 +1502,36 @@ class Horde_Ldap } /** - * Get the LDAP link resource. It will loop attempting to - * re-establish the connection if the connection attempt fails and - * auto_reconnect has been turned on (see the _config array documentation). + * Returns the LDAP link resource. * - * @access public - * @return resource LDAP link + * It will loop attempting to re-establish the connection if the + * connection attempt fails and auto_reconnect has been turned on + * (see the _config array documentation). + * + * @return resource LDAP link. */ - public function &getLink() + public function getLink() { if ($this->_config['auto_reconnect']) { while (true) { - // - // Return the link handle if we are already connected. Otherwise - // try to reconnect. - // - if ($this->_link !== false) { + /* Return the link handle if we are already connected. + * Otherwise try to reconnect. */ + if ($this->_link) { return $this->_link; - } else { - $this->performReconnect(); } + $this->performReconnect(); } } return $this->_link; } - /** - * Return a boolean expression using the specified operator. + /** + * Builds an LDAP search filter fragment. * * @param string $lhs The attribute to test. * @param string $op The operator. * @param string $rhs The comparison value. - * @param array $params Any additional parameters for the operator. @since - * Horde 3.2 + * @param array $params Any additional parameters for the operator. * * @return string The LDAP search fragment. */ @@ -1717,21 +1541,23 @@ class Horde_Ldap case 'LIKE': if (empty($rhs)) { return '(' . $lhs . '=*)'; - } elseif (!empty($params['begin'])) { - return sprintf('(|(%s=%s*)(%s=* %s*))', $lhs, Horde_LDAP::quote($rhs), $lhs, Horde_LDAP::quote($rhs)); - } elseif (!empty($params['approximate'])) { - return sprintf('(%s=~%s)', $lhs, Horde_LDAP::quote($rhs)); } - return sprintf('(%s=*%s*)', $lhs, Horde_LDAP::quote($rhs)); + if (!empty($params['begin'])) { + return sprintf('(|(%s=%s*)(%s=* %s*))', $lhs, self::quote($rhs), $lhs, self::quote($rhs)); + } + if (!empty($params['approximate'])) { + return sprintf('(%s=~%s)', $lhs, self::quote($rhs)); + } + return sprintf('(%s=*%s*)', $lhs, self::quote($rhs)); default: - return sprintf('(%s%s%s)', $lhs, $op, Horde_LDAP::quote($rhs)); + return sprintf('(%s%s%s)', $lhs, $op, self::quote($rhs)); } } /** - * Escape characters with special meaning in LDAP searches. + * Escapes characters with special meaning in LDAP searches. * * @param string $clause The string to escape. * @@ -1745,16 +1571,17 @@ class Horde_Ldap } /** - * Take an array of DN elements and properly quote it according to RFC + * Takes an array of DN elements and properly quotes it according to RFC * 1485. * * @param array $parts An array of tuples containing the attribute * name and that attribute's value which make * up the DN. Example: - * - * $parts = array(0 => array('cn', 'John Smith'), - * 1 => array('dc', 'example'), - * 2 => array('dc', 'com')); + * + * $parts = array(0 => array('cn', 'John Smith'), + * 1 => array('dc', 'example'), + * 2 => array('dc', 'com')); + * * * @return string The properly quoted string DN. */ -- 2.11.0