<?php

/**
* This is the class for the LDAP Operation.
*
* @package Library
* @subpackage Ldap
*/
class Ldap
{

	/**
	* Where any database errors are stored.
	*
	* @see GetError
	* @see setError
	*
	* @access private
	*
	* @var String
	*/
	var $_Error = null;

	/**
	* What type of error this is.
	*
	* @see GetError
	* @see setError
	*
	* @access private
	*
	* @var String
	*/
	var $_ErrorLevel = E_USER_ERROR;

	/**
	* LDAP Host - this can be ip address or domain name
	* 
	* @access private
	* 
	* @var string
	*/
	var $_ldapHost	= '';

	/**
	* LDAP Port
	* 
	* @access private
	* 
	* @var Integer
	*/
	var $_ldapPort	= 0;

	/**
	* Connect username - the authorized username, which allow us to Bind and perform operation on LDAP Server
	* 
	* @access private
	* 
	* @var String
	*/
	var $_username	= '';

	/**
	* Password of Connect User
	* 
	* @access private
	* 
	* @var String
	*/
	var $_password	= '';

	/**
	* Base DN - The start tree of where this LDAP operates
	* 
	* @access private
	* 
	* @var String
	*/
	var $_baseDn	= '';

	/**
	* LDAP Version 3 - Do we want to connect to LDAP as LDAP Version 3?
	* 
	* @access private
	* 
	* @var boolean
	*/
	var $_ldapVersion3	= false;

	/**
	* Negotiate TLS - Do we want to connect to LDAP securely using TLS
	* 
	* @access private
	* 
	* @var boolean
	*/
	var $_negotiateTLS	= false;

	/**
	* Follow Referral Option
	* 
	* @access private
	* 
	* @var Character
	*/
	var $_followReferral	= 0;

	/**
	* The connection for this LDAP Class
	* 
	* @access private
	* 
	* @var Object
	*/
	var $_connection	= false;

	/**
	* Ldap
	* Constructor for Ldap class
	*
	* @param String		$ldapHost 		LDAP Server Host (hostname or ip address)
	* @param Integer	$ldapPort 		LDAP Server Port
	* @param String		$username 		LDAP Connect Username - Authorized User to connect to LDAP Server
	* @param String		$password 		Password for Connect Username
	* @param String		$baseDn 		The Base DN where our LDAP will operate from
	* @param Boolean	$ldapVersion3 	The Indication flag whether this LDAP Connection is using LDAP Version 3 connection.
	* @param Boolean 	$negotiateTLS 	The Indication flag whether this LDAP Connection is using TLS Negotiation.
	* @param Integer	$followReferral The option for Referral for this LDAP Connection
	*
	* @return Mixed		return not success return null 
	*/
	function Ldap (	$ldapHost, $ldapPort, $username, $password, $baseDn , $ldapVersion3, $negotiateTLS, $followReferral)
	{
		if ($this->hasLdapExtension()) {

			$this->_ldapHost 		= $ldapHost;
			$this->_ldapPort 		= $ldapPort;
			$this->_username 		= $username;
			$this->_password 		= $password;
			$this->_baseDn 			= $baseDn;
			$this->_ldapVersion3 	= $ldapVersion3;
			$this->_negotiateTLS 	= $negotiateTLS;
			$this->_followReferral 	= $followReferral;
			if (!$this->connect()) {
				return null;
			}
		}
		return null;
	}

	/**
	* connect
	* Connect to LDAP Server
	*
	* @return Boolean If all values are valid, connect to LDAP Server successfully and set all the option sucessfully then return true. Otherwise, return false
	*/
	function connect ()
	{
		if ($this->_ldapHost != '' && $this->_ldapPort != '') {
			$this->_connection = @ldap_connect($this->_ldapHost, $this->_ldapPort);
			if ($this->_connection)	{
				if ($this->_ldapVersion3) {
					if (!@ldap_set_option($this->_connection, LDAP_OPT_PROTOCOL_VERSION, 3)) {
						$this->setError('Error: Unable to set option to use LDAP Version 3');
						return false;
					}
				}
				if (!@ldap_set_option($this->_connection, LDAP_OPT_REFERRALS, (int) $this->_followReferral)) {
					$this->setError('Error: Unable to set follow referrals option for LDAP Connection');
					return false;
				}
				if ($this->_negotiateTLS) {
					if (!@ldap_start_tls($this->_connection)) {
						$this->setError('Error: Unable to Negotiate TLS for LDAP Connection');
						return false;
					}
				}
				return true;
			}
			$this->setError('Error: Unable to connect to LDAP Server');
			return false;
		}
		$this->setError('Error: Invalid LDAP Host or Port');
		return false;
	}

	/**
	* bind
	* Bind to LDAP using specified Username/dn, Password.
	*
	* @param String $username	Custom Username if username & password are supplied as paras. Otherwise it uses class username variable
	* @param String	$password	Custom Password if username & password are supplied as paras. Otherwise it uses class password variable
	*
	* @return Boolean If user is binded successfully, return true. Otherwise, return false
	*/
	function bind ($username = '', $password = '')
	{
		$privegelesBind = false;
		if ($username == '' && $password == '') {
			$username = $this->_username;
			$password = $this->_password;
		}
		$bindResult = @ldap_bind($this->_connection, $username, $password);
		if (!$bindResult) {
			$this->setError('Error: Could not Bind to LDAP Server, Please ensure the LDAP Host, Connect Username & Password are correct');
			return false;
		}
		return $bindResult;
	}

	/**
	* generalSearch
	* Search data (based on the supplied $attributes) for user/s (based on the $filter param) start from the $baseDn param
	*
	* @param String		$filter						The search criteria
	* @param Array		$attributes					The LDAP Attribute we expected for this Search, all results will be returned if array is empty
	* @param String		$baseDn						The Base DN where we want to have the search begin
	*
	* @return Mixed		If the search result has more than 0 entry, return a multi dimension array containing the results. Otherwise, return false.
	*/
	function search($filter, $attributes = array(), $baseDn = '')
	{
		$finalResult = false;
		if ($baseDn == '') {
			$baseDn = $this->_baseDn;
		}
		$result = @ldap_search($this->_connection, $baseDn, $filter);
		$records = @ldap_get_entries($this->_connection, $result);

		if (sizeof($attributes)) {
			for ($i=0; $i < $records["count"]; $i++)
			{
				foreach ($attributes as $attribute) {
					$attribute = strtolower($attribute);
					$finalResult[$i][$attribute] = '';
					if (isset($records[$i][$attribute])) {
						for ($j = 0; $j < sizeof($records[$i][$attribute])-1; $j++) {
							$finalResult[$i][$attribute][] = $records[$i][$attribute][$j];
						}
					}
					// Always return the dn
					if (isset($records[$i]["dn"])) {
						$finalResult[$i]['dn'] = $records[$i]["dn"];
					}
				}
			}
			return $finalResult;
		}
		return $records;
	}

	/**
	* modify
	* Modify the attributes value for the specified DN
	*
	* @param String		$dn				Distinguish name - unique name for ldap entry	
	* @param Array		$attributes		A list of new attribute values.
	*
	* @return Boolean	Return true if the entry's attributes are modified successfully
	*/
	function modify ($dn, $attributes)
	{
		if (!@ldap_modify ($this->_connection, $dn, $attributes)) {
			$this->setError('Failed to modify user attribute');
			return false;
		}
		return true;
	}

	/**
	* setAccountStatus
	* Set the account status for an entry in LDAP
	*
	* @return Boolean	Always return false in base class
	*/
	function setAccountStatus () {
		$this->SetError('Cannot call base class method setAccountStatus directly');
		return false;
	}

	/**
	* getStatus
	* Get the status from the specified LDAP entry
	*
	* @return Boolean	Always return false in base class
	*/
	function getAccountStatus ()
	{
		$this->SetError('Cannot call base class method setAccountStatus directly');
		return false;
	}

	/**
	* isStatusEnable
	* To check if the returned status is Enable or Disable
	*
	* @return	Boolean	Always return false in base class
	*/
	function isStatusEnable()
	{
		$this->SetError('Cannot call base class method isStatusEnable directly');
		return false;
	}
	/**
	* getAccountStatusAttribute
	* Get the Attribute name that hold the user account status
	*
	* @return	String	Return the attribute of user account status
	*/
	function getAccountStatusAttribute() {
		$this->SetError('Cannot call base class method getAccountStatusAttribute directly');
		return false;
	}
	
	/**
	* close
	* Close the LDAP connection
	*
	* @return Void
	*/
	function close ()
	{
		ldap_close($this->_connection);
	}

	/**
	* delete
	* Delete the specified entry from LDAP
	*
	* @param String		$dn		Distinguish name - unique name for ldap entry
	*
	* @return Boolean	Return true if the specified entry is deleted successfully. Otherwise, return false
	*/
	function delete ($dn)
	{
		if(!@ldap_delete($this->_connection, $dn)) {
			$this -> setError('Error: When try to delete entry in LDAP');
			return false;
		}
		return true;
	}

	/**
	* add
	* Add new entry to LDAP Server
	*
	* @param String		$dn				Distinguish name - unique name for ldap entry
	* @param Array		$newAttributes	The attributes for the new entry
	*
	* @return Boolean	Return true if the specified entry is added successfully. Otherwise, return false
	*/
	function add ($dn, $newAttributes)
	{
		if (!@ldap_add($this->_connection, $dn, $newAttributes)) {
			$this -> setError('Error: When try to add new entry in LDAP');
			return false;
		}
		return true;
	}

	/**
	* hasLdapExtension
	* A function to check if the our PHP has LDAP support
	*
	* @return Boolean	Return false if the PHP does not have LDAP Extension installed. Otherwise, return true
	*/
	function hasLdapExtension() {
		if (!function_exists('ldap_connect')) {
			$this->setError('Your PHP installation does not have LDAP support. Please enable LDAP support in PHP or ask your web host to do so for you.');
			return false;
		}
		return true;
	}

	/**
	* setError
	*
	* Stores the error in the _error var for retrieval.
	*
	* @param String $error The error you wish to store for retrieval.
	* @param String $errorlevel The error level you wish to store.
	*
	* @return Void Doesn't return anything, only sets the values and leaves it at that.
	*/
	function setError($error='', $errorlevel=E_USER_ERROR)
	{
		$this->_Error = $error;
		$this->_ErrorLevel = $errorlevel;
	}

	/**
	* getErrorMsg
	*
	* This simply returns the $_Error var
	*
	* @access public
	*
	* @see setError
	*
	* @return String Returns the error
	*/
	function getErrorMsg()
	{
		return $this->_Error;
	}
}

?>