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

/**
 * This class takes care of searching within LDAP
 *
 * @package    PLA
 * @subpackage LDAP/Search
 * @category   Helpers
 * @author     Deon George
 * @copyright  (c) phpLDAPadmin Development Team
 * @license    http://dev.phpldapadmin.org/license.html
 */
abstract class PLA_Database_LDAP_Search {
	private $_r;				// Our LDAP Server to query
	private $_scope = 'base';		// LDAP Search Scope
	private $_filter = '(objectclass=*)';	// LDAP Search Scope
	private $_attrs = array('*','+');	// LDAP Attributes to Return
	private $_base = '';			// LDAP Base to Search
	private $_db_pending = array();		// LDAP Query Filter to compile

	/**
	 * Callable database methods
	 * @var array
	 */
	protected static $_db_methods = array(
		'where', 'and_where', 'or_where', 'where_open', 'and_where_open', 'or_where_open', 'where_close',
		'and_where_close', 'or_where_close',
	);

	/**
	 * Members that have access methods
	 * @var array
	 */
	protected static $_properties = array(
	);

	public function __construct($resource) {
		$this->_r = $resource;
	}

	/**
	 * Handles pass-through to database methods. Calls to query methods
	 * (query, get, insert, update) are not allowed. Query builder methods
	 * are chainable.
	 *
	 * @param   string  $method Method name
	 * @param   array   $args   Method arguments
	 * @return  mixed
	 */
	public function __call($method,array $args) {
		if (in_array($method,Database_LDAP_Search::$_properties)) {
			/*
			// @todo To Implement
			if ($method === 'validation')
			{
				if ( ! isset($this->_validation))
				{
					// Initialize the validation object
					$this->_validation();
				}
			}
			*/

			// Return the property
			return $this->{'_'.$method};
		}
		elseif (in_array($method,Database_LDAP_Search::$_db_methods))
		{
			// Add pending database call which is executed after query type is determined
			$this->_db_pending[] = array('name' => $method,'args' => $args);

			return $this;
		}
		else
		{
			throw new Kohana_Exception('Invalid method :method called in :class',
				array(':method' => $method,':class' => get_class($this)));
		}
	}

	private function _build() {
		$s = Database_LDAP_Search::Search();

		// Process pending database method calls
		foreach ($this->_db_pending as $method) {
			$name = $method['name'];
			$args = $method['args'];

			$this->_db_applied[$name] = $name;

			call_user_func_array(array($s,$name),$args);
		}

		return $s;
	}

	public static function Search($columns = NULL) {
		return new Database_LDAP_Search_Builder_Query(func_get_args());
	}

	public static function instance($resource) {
		return new Database_LDAP_Search($resource);
	}

	public function scope($val) {
		switch ($val) {
			case 'base':
			case 'sub':
			case 'one': $this->_scope = $val;
				break;

			default:
				throw new Kohana_Exception('Unknown search scope :scope',array(':scope',$val));
		}

		return $this;
	}

	/**
	 * Search the LDAP database
	 */
	public function run() {
		$query = array();

		// Compile our query
		if ($this->_db_pending)
			$this->_filter = $this->_build();

		/*
		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
		*/

		$attrs_only = 0;

		$this->_base = 'o=Simpsons';
		/*
		// @todo To implement
		if (! isset($query['base'])) {
			$bases = $this->getBaseDN();
			$query['base'] = array_shift($bases);
		}
		*/

		/*
		// @todo To implement
		if (! isset($query['deref']))
			$query['deref'] = $_SESSION[APPCONFIG]->getValue('deref','search');
		*/
		if (! isset($query['size_limit']))
			$query['size_limit'] = 0;
		if (! isset($query['time_limit']))
			$query['time_limit'] = 0;

		/*
		if ($query['scope'] == 'base' && ! isset($query['baseok']))
			system_message(array(
				'title'=>sprintf('Dont call %s',__METHOD__),
				'body'=>sprintf('Use getDNAttrValues for base queries [%s]',$query['base']),
				'type'=>'info'));
		*/

		/*
		if (is_array($query['base'])) {
			system_message(array(
				'title'=>_('Invalid BASE for query'),
				'body'=>_('The query was cancelled because of an invalid base.'),
				'type'=>'error'));

			return array();
		}
		*/

		/*
		if (DEBUG_ENABLED)
			debug_log('%s search PREPARE.',16,0,__FILE__,__LINE__,__METHOD__,$query['scope']);
		*/

		/*
		if ($debug)
			debug_dump(array('query'=>$query,'server'=>$this->getIndex(),'con'=>$this->connect($method)));
		*/

		//$resource = $this->connect($method,$debug);

		switch ($this->_scope) {
			case 'base':
				$search = @ldap_read($this->_r,$this->_base,$this->_filter,$this->_attrs,$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
				break;

			case 'one':
				$search = @ldap_list($this->_r,$this->_base,$this->_filter,$this->_attrs,$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
				break;

			case 'sub':
			default:
				$search = @ldap_search($this->_r,$this->_base,$this->_filter,$this->_attrs,$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
				break;
		}

		/*
		if ($debug)
			debug_dump(array('method'=>$method,'search'=>$search,'error'=>$this->getErrorMessage()));
		*/

		/*
		if (DEBUG_ENABLED)
			debug_log('Search scope [%s] base [%s] filter [%s] attrs [%s] COMPLETE (%s).',16,0,__FILE__,__LINE__,__METHOD__,
				$query['scope'],$query['base'],$query['filter'],$query['attrs'],is_null($search));
		*/

		if (! $search)
			return array();

		$return = array();

		// Get the first entry identifier
		if ($entries = ldap_get_entries($this->_r,$search)) {
			# Remove the count
			if (isset($entries['count']))
				unset($entries['count']);

			// Iterate over the entries
			foreach ($entries as $a => $entry) {
				/*
				if (! isset($entry['dn']))
					debug_dump_backtrace('No DN?',1);
				*/

				// Remove the none entry references.
				if (! is_array($entry)) {
					unset($entries[$a]);
					continue;
				}

				$dn = $entry['dn'];
				unset($entry['dn']);

				// Iterate over the attributes
				foreach ($entry as $b => $attrs) {
					// Remove the none entry references.
					if (! is_array($attrs)) {
						unset($entry[$b]);
						continue;
					}

					// Remove the count
					if (isset($entry[$b]['count']))
						unset($entry[$b]['count']);
				}

				// Our queries always include the DN (the only value not an array).
				$entry['dn'] = $dn;
				$return[$dn] = $entry;
			}

			// Sort our results
			foreach ($return as $key => $values)
				ksort($return[$key]);
		}

		/*
		if (DEBUG_ENABLED)
			debug_log('Returning (%s)',17,0,__FILE__,__LINE__,__METHOD__,$return);
		*/

		return $return;
	}
}
?>
