<?php defined('SYSPATH') or die('No direct access allowed.');

/**
 * This class takes care of communicating with LDAP
 *
 * @package    PLA
 * @subpackage LDAP
 * @category   Helpers
 * @author     Deon George
 * @copyright  (c) phpLDAPadmin Development Team
 * @license    http://dev.phpldapadmin.org/license.html
 */
abstract class PLA_Database_LDAP extends Database {
	// Our required abstract functions
        public function set_charset($charset) {}
        public function query($type, $sql, $as_object = FALSE, array $params = NULL) {}
        public function begin($mode = NULL) {}
        public function commit() {}
        public function rollback() {}
        public function list_tables($like = NULL) {}
        public function list_columns($table, $like = NULL, $add_prefix = TRUE) {}
        public function escape($value) { return $value;}

	// Overrides
	public function quote_column($column) {
		return $column;
	}

	// This function will enable us to have multiple resource contexts
	// @todo To Implement
	public function select_db($x) {
		return $this;
	}

	private function _connect() {
		/*
		// @todo To implement
		# No identifiable connection exists, lets create a new one.
		if (DEBUG_ENABLED)
			debug_log('Creating NEW connection [%s] for index [%s]',16,0,__FILE__,__LINE__,__METHOD__,
				$method,$this->index);
		*/

		/*
		// @todo To implement
		if (function_exists('run_hook'))
			run_hook('pre_connect',array('server_id'=>$this->index,'method'=>$method));
		*/

		if (! empty($this->_config['port']))
			$r = ldap_connect($this->_config['connection']['hostname'],$this->_config['port']);
		else
			$r = ldap_connect($this->_config['connection']['hostname']);

		/*
		// @todo To implement
		if (DEBUG_ENABLED)
			debug_log('LDAP Resource [%s], Host [%s], Port [%s]',16,0,__FILE__,__LINE__,__METHOD__,
				$this->_r,$this->getValue('server','host'),$this->getValue('server','port'));
		*/

		if (! is_resource($r))
			throw Kohana_Exception('UNHANDLED, $r is not a resource');

		// Go with LDAP version 3 if possible (needed for renaming and Novell schema fetching)
		ldap_set_option($r,LDAP_OPT_PROTOCOL_VERSION,3);

		/* Disabling this makes it possible to browse the tree for Active Directory, and seems
		 * to not affect other LDAP servers (tested with OpenLDAP) as phpLDAPadmin explicitly
		 * specifies deref behavior for each ldap_search operation. */
		ldap_set_option($r,LDAP_OPT_REFERRALS,0);

		/*
		// @todo To implement
		# Try to fire up TLS is specified in the config
		if ($this->isTLSEnabled())
			$this->startTLS($this->_r);
		*/

		return $r;
	}

	private function _bind($r,$u,$p) {
		if (! is_resource($r))
			throw Kohana_Exception('UNHANDLED, $r is not a resource');

		/*
		// @todo To implement
		# If SASL has been configured for binding, then start it now.
		if ($this->isSASLEnabled())
			$br = $this->startSASL($this->_r,$method);

		# Normal bind...
		else
		*/
			$br = @ldap_bind($r,$u,$p);

		/*
		if ($debug)
			debug_dump(array('method'=>$method,'bind'=>$bind,'USER'=>$_SESSION['USER']));

		if (DEBUG_ENABLED)
			debug_log('Resource [%s], Bind Result [%s]',16,0,__FILE__,__LINE__,__METHOD__,$this->_r,$bind);
		*/

		if (! $br) {
			/*
			if (DEBUG_ENABLED)
				debug_log('Leaving with FALSE, bind FAILed',16,0,__FILE__,__LINE__,__METHOD__);
			*/

			$this->noconnect = true;

			/*
			// @todo To implement
			system_message(array(
				'title'=>sprintf('%s %s',_('Unable to connect to LDAP server'),$this->getName()),
				'body'=>sprintf('<b>%s</b>: %s (%s) for <b>%s</b>',_('Error'),$this->getErrorMessage($method),$this->getErrorNum($method),$method),
				'type'=>'error'));
			*/

		} else {
			$this->noconnect = false;

			/*
			// @todo To implement
			# If this is a proxy session, we need to switch to the proxy user
			if ($this->isProxyEnabled() && $bind['id'] && $method != 'anon')
				if (! $this->startProxy($this->_r,$method)) {
					$this->noconnect = true;
					$CACHE[$this->index][$method] = null;
				}
			*/
		}

		/*
		// @todo To implement
		if (function_exists('run_hook'))
			run_hook('post_connect',array('server_id'=>$this->index,'method'=>$method,'id'=>$bind['id']));
		*/

		/*
		// @todo To implement
		if ($debug)
			debug_dump(array($method=>$CACHE[$this->index][$method]));
		*/

		return $br;
	}

	public function connect() {
		if ($this->_r = $this->_connect())
			return $this;
		else
			throw Kohana_Exception('Unable to connect to LDAP Server?');
	}

	public function bind($user,$pass) {
		// If this is an anon query, then we return

		// Do we need to do an anon search to find the DN
		if (! empty($this->_config['login_attr']) AND strtoupper($this->_config['login_attr']) != 'DN') {
			$u = $this->search()
				->scope('sub')
				->where($this->_config['login_attr'],'=',$user)
				->run();

			if (! $u)
				throw new Kohana_Exception('Unable to find user :user',array(':user'=>$user));

			$u = array_shift($u);
			$user = $u['dn'];
		}

		// Bind
		if ($this->_bind($this->_r,$user,$pass))
			return $this;
		else
			throw new Kohana_Exception('Unable to bind');
	}

	public function search() {
		return new Database_LDAP_Search($this->_r);
	}
}
?>
