<?php
/**
 * Authentication class
 * 
 * Provides methods related to authentication and security
 * 
 * @package linea21\modules\auth
 * @author $Author$ - Simon Georget <simon@linea21.com>
 * @version $Id$ 
 * @access public
 * @license http://opensource.org/licenses/gpl-3.0.html
 * Plugin Management
 */

class auth {


	protected $dispatcher = null;

	public function __construct() {
		$this->dispatcher = $GLOBALS['dispatcher'];
	}

	public function __call($method, $arguments) {
		$event = $this->dispatcher->notifyUntil(new sfEvent($this, 'auth.extensible_function', array(
				'method'    => $method,
				'arguments' => $arguments
		)));
		if (!$event->isProcessed())
		{
			throw new Exception(sprintf('Call to undefined method %s::%s.', get_class($this), $method));
		}

		return $event->getReturnValue();
	}

	/**
	 * AuthenthificationProcess()
	 * Authentification d'un utilisateur
	 *
	 * @param  $login
	 * @param  $pass
	 * @param  $type ='PUBLIC' || 'ADMIN'
	 * @return boolean (true) ou message d'erreur
	 */
	public function logIn($login, $pass, $type, $rememberme)
	{

		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.logIn', array('login' => $login, 'password' => $pass, 'type' => $type)));

		return $this->builtInAuth($login, $pass, $type, $rememberme);

	}

	protected function builtInAuth($login, $pass, $type, $rememberme) {

		$src = LOG_ACCESS;
		
		list($login, $pass) = $GLOBALS['sql_object']->DBEscape(array($login, $pass));
		
		// we get user Password salt
		$data = $GLOBALS['sql_object']->DBSelect(SQL_get_UserInfo($login));

		if(isset($data[0]['salt'])) $salt = $data[0]['salt'];
		else $salt = ''; // will throw an error later
		
		$crypt_pass = crypt($pass, $salt);

		$data = $GLOBALS['sql_object']->DBSelect(SQL_Get_UserInfo4Auth($login, $crypt_pass));

		if ($data != 0 && count($data) == 1) {

			$this->initSession($data, $login);

			if($rememberme) {
				$this->setCookieRememberMe();
			}

			if ($type == 'ADMIN') {
				// si est simple utilisateur
				if (!array_search ('A' , $_SESSION['userright']) && !array_search ('O' , $_SESSION['userright'])) {

					$this->logOut();

					logfile($src, array($type, $login, 'unauthorized', __METHOD__, $_SERVER['HTTP_USER_AGENT'], get_referer(), $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['QUERY_STRING'], $_SERVER['HTTP_COOKIE'], i2c_realip()));
						
					return _t('divers','errorauth');
				}
			}

			logfile($src, array($type, $login, 'success', __METHOD__, $_SERVER['HTTP_USER_AGENT'], get_referer(), $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['QUERY_STRING'], $_SERVER['HTTP_COOKIE'], i2c_realip()));

			
			return true;
			
		} else {

			logfile($src, array($type, $login, 'failed', __METHOD__, $_SERVER['HTTP_USER_AGENT'], get_referer(), $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['QUERY_STRING'], $_SERVER['HTTP_COOKIE'], i2c_realip()));
			
			$error_msg = _t('divers','errorauth');
			return $error_msg;
			
		}

	}

	/**
	 * isSimpleUser()
	 *
	 * @return boolean
	 */
	public function isSimpleUser() {
		if(!isset($_SESSION['userright'])) return false;

		if(!in_array('A', $_SESSION['userright']) && !in_array('O', $_SESSION['userright'])) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * isSuperAdmin()
	 *
	 * @return boolean
	 */
	public function isSuperAdmin() {
		return $this->hasRight('level');
	}

	/**
	 * logOut()
	 *
	 * @return boolean
	 */
	public function logOut() {

		$_SESSION = array();
		session_unset();
		session_destroy();
		$this->destroyUserCookie();

		if(isset($_COOKIE['linea21_uname'])) {

			list($login, $cookiehash) = explode ('_' , $_COOKIE['linea21_uname']);

			// we clean user_cookiehash field is set
			$GLOBALS['sql_object']->DBQuery('UPDATE l21_user SET user_cookiehash="" WHERE user_cookiehash = "' . $GLOBALS['sql_object']->DBEscape($cookiehash) . '" AND lower(user_login) = "'. $GLOBALS['sql_object']->DBEscape($login) . '" ;');
			setcookie('linea21_uname', '', (time() - 3600), '/');
		}
		
	}
	/**
	 * hasRight()
	 *
	 * @param string $item
	 * @return bool
	 */
	public function hasRight($item, $subitem = false)
	{
		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.has_right', array('element' => $item)));

		switch ($item) {
			case 'category_user':
				if ($this->GetSessionElement('right_category_user') != 'A') return false;
				else return true;
			case 'level':
				if ($this->GetSessionElement('right_level') != 'A') return false;
				else return true;
			case 'scale':
				if ($this->GetSessionElement('right_scale') != 'A') return false;
				else return true;
			case 'yellowpages':
				if ($this->GetSessionElement('right_yellowpages') != 'A') return false;
				else return true;
			case 'news':
				if ($this->GetSessionElement('right_news') != 'U') return true;
				else return false;
			case 'project':
				if ($this->GetSessionElement('right_project') == 'A' || $this->GetSessionElement('right_project') == 'O') return true;
				else return false;
			case 'publication':
				if ($this->GetSessionElement('right_publication') != 'U') return true;
				else return false;
			case 'workshop':
				if ($this->GetSessionElement('right_workshop') != 'U') return true;
				else return false;
			case 'workshoprep':
				if ($this->GetSessionElement('right_workshop') != 'U') return true;
				else return false;
			case 'dashboard':
				if ($this->GetSessionElement('right_dashboard') != 'U') return true;
				else return false;
			case 'indicator':
				if (in_array($subitem, $this->GetSessionElement('indicators'))) return true;
				else return false;
			default:
				return false;
		}
	}


	 /**
	 * initSession()
	 * Init user session
	 *
	 * @param  $data
	 * @return boolean (true) ou message d'erreur
	 */
	public function initSession($data, $login) {

		// préparation du tableau de droits
		$right['dashboard'] = $data[0]['rights_dashboard'];
		$right['workshop'] = $data[0]['rights_workshop'];
		$right['project'] = $data[0]['rights_project'];
		$right['publication'] = $data[0]['rights_publication'];
		$right['news'] = $data[0]['rights_news'];
		$right['yellowpages'] = $data[0]['rights_yellowpages'];
		$right['level'] = $data[0]['rights_level'];
		$right['scale'] = $data[0]['rights_scale'];
		$right['category_user'] = $data[0]['rights_category_user'];
		
		$_SESSION['authenticated'] = true;
		$_SESSION['userid'] = base64_encode($data[0]['user_id']);
		$_SESSION['userwhois'] = base64_encode($data[0]['user_password']);
		$_SESSION['userlogin'] = $login;
		$_SESSION['lastcon'] = $data[0]['user_last_con'];
		$_SESSION['logout_token'] = md5(uniqid(rand(), true));
		
		if(!empty($data[0]['profile_firstname'])) {
			$_SESSION['userfirstname'] = $data[0]['profile_firstname'];
		}
		if(!empty($data[0]['profile_lastname'])) {
			$_SESSION['userlastname'] = $data[0]['profile_lastname'];
		}
		$_SESSION['userright'] = $right;
		
		$_SESSION['projects'] = $this->retrieveUserProjects(array('login' => $login, 'pass' => $data[0]['user_password']));
		$_SESSION['indicators'] = $this->retrieveProjectsIndicators($_SESSION['projects']);
		
		$workgroups = $this->retrieveUserWorkgroups(array('login' => $login, 'pass' => $data[0]['user_password']));
		
		$table['workshop'] = base64_encode($workgroups);
		$table['lifetime'] = time() + 3600 * 720;
		
		$this->destroyUserCookie();
			
		// we update the last connexion field
		$GLOBALS['sql_object']->DBQuery('UPDATE l21_user SET user_last_con = NOW() where user_id=' . $data[0]['user_id'] . ';');
		
		// Filter data event + return value
		$r = $this->dispatcher->filter(new sfEvent(__FUNCTION__, 'auth.extend_session', array('data' => $table)), $table);
		$table = $r->getReturnValue();
		
		$this->setUserCookie($table);

		return true;
	}

	/**
	 * GetSessionElement()
	 *
	 * @param string $item
	 * @return $result
	 */

	public function GetSessionElement($item)	{
		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.get_session_element', array('element' => $item)));

		switch ($item) {
			case 'id':
				return base64_decode($_SESSION['userid']);
				break;
			case 'whois':
				return base64_decode($_SESSION['userwhois']);
				break;
			case 'login':
				return $_SESSION['userlogin'];
				break;
			case 'firstname':
				if(isset($_SESSION['userfirstname']))
					return $_SESSION['userfirstname'];
				break;
			case 'lastname':
				if(isset($_SESSION['userlastname']))
					return $_SESSION['userlastname'];
				break;
			case 'projects':
				return $_SESSION['projects'];
				break;
			case 'indicators':
				return $_SESSION['indicators'];
				break;
			case 'right_level':
				return $_SESSION['userright']['level'];
				break;
			case 'right_dashboard':
				return $_SESSION['userright']['dashboard'];
				break;
			case 'right_workshop':
				return $_SESSION['userright']['workshop'];
				break;
			case 'right_project':
				return $_SESSION['userright']['project'];
				break;
			case 'right_publication':
				return $_SESSION['userright']['publication'];
				break;
			case 'right_news':
				return $_SESSION['userright']['news'];
				break;
			case 'right_yellowpages':
				return $_SESSION['userright']['yellowpages'];
				break;
			case 'right_scale':
				return $_SESSION['userright']['scale'];
				break;
			case 'right_category_user':
				return $_SESSION['userright']['category_user'];
				break;
			case 'logout_token':
			    return $_SESSION['logout_token'];
			    break;
			default:
				return false;
				break;
		}
	}

	/**
	 * isWorkgroupUser()
	 * check if the current user belongs to a workgroup
	 * used in PUBLIC app
	 * @param  $workshop_id
	 * @return boolean
	 */
	public function isWorkgroupUser($workshop_id, $sql_object)
	{
		// check if user has cookie, if not we exit
		if(!$this->isAuthenticated()) return false;

		// if is SuperAdmin we allow the access
		if($this->isSuperAdmin()) return true;

		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.is_workgroup_user', array('id' => $workshop_id)));

		include_once('../admin/'.SQL.'.inc.php');

		// 1 -we check that login and password stored in sessions are corrects
		$q = SQL_Get_UserInfo4Auth($this->GetSessionElement('login'), $this->GetSessionElement('whois'));
		$data = $sql_object->DBSelect($q);

		if ($data != 0 && count($data) == 1 && $data[0]['user_id'] == $this->GetSessionElement('id')) {
			// 2 - if ok,  we check that the user belongs to the given group
			$r = $sql_object->DBSelect(SQL_Get_isWorkgroupUser($data[0]['user_id']));

			for ($i = 0; $i < count($r); $i++) {
				if ($workshop_id == $r[$i]['jwu_workshop_id']) {
					return true;
				}

			}
		}
		return false;
	}
	
	/**
	 * isWorkgroupPendingUser()
	 * check if the current user is waiting for 
	 * workgroup subscription approval
	 * used in PUBLIC app
	 * @param  $workshop_id
	 * @return boolean
	 */
	public function isWorkgroupPendingUser($workshop_id, $sql_object)
	{
		// check if user has cookie, if not we exit
		if(!$this->isAuthenticated()) return false;
	
		// if is SuperAdmin we allow the access
		if($this->isSuperAdmin()) return true;
	
		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.is_workgroup_user', array('id' => $workshop_id)));
	
		include_once('../admin/'.SQL.'.inc.php');
	
		// 1 -we check that login and password stored in sessions are corrects
		$q = SQL_Get_UserInfo4Auth($this->GetSessionElement('login'), $this->GetSessionElement('whois'));
		$data = $sql_object->DBSelect($q);
	
		if ($data != 0 && count($data) == 1 && $data[0]['user_id'] == $this->GetSessionElement('id')) {
			// 2 - if ok,  we check that the user belongs to the given group
			$r = $sql_object->DBSelect(SQL_Get_isWorkgroupPendingUser($data[0]['user_id']));
	        
			if(is_array($r)) {
    			for ($i = 0; $i < count($r); $i++) {
    				if ($workshop_id == $r[$i]['jwu_workshop_id']) {
    					return true;
    				}
    			}
			}
		}
		return false;
	}
	
	/**
	 * isProjectManager()
	 * check if a given user is project manager
	 * used in ADMIN app
	 * @param  $id_user
	 * @param  $sql_object
	 * @param  $id_project
	 * @return boolean
	 */
	public function isProjectManager($id_user, $sql_object, $id_project)
	{
		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.is_project_manager', array('id' => $id_user, 'project_id' => $id_project)));
	
		$data = $sql_object->DBSelect(SQL_Get_isProjectManager($id_user));
	
		for ($i = 0; $i < count($data); $i++) {
			if ($id_project == $data[$i]['jpm_project_id'])
				return true;
		}
		return false;
	}

	/**
	 * isWorkgroupOrganiser()
	 * check if a given user is workgroup organiser
	 * used in ADMIN app
	 * @param  $id_user
	 * @param  $sql_object
	 * @param  $id_workshop
	 * @return boolean
	 */
	public function isWorkgroupOrganiser($id_user, $sql_object, $id_workshop)
	{
		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.is_workgroup_organiser', array('id' => $id_user, 'workgroup_id' => $id_workshop)));

		$data = $sql_object->DBSelect(SQL_Get_isWorkgroupOrganiser($id_user));

		if(is_array($data)) {
    		for ($i = 0; $i < count($data); $i++) {
    			if ($id_workshop == $data[$i]['jwu_workshop_id'])
    				return true;
    		}
		}
		return false;
	}

	public function isAuthenticated() {
		if(isset($_SESSION['authenticated'])) return true;
		else return false;
	}

	/**
	 * identifyWithLogin()
	 * Authentification d'un utilisateur
	 * par son login seulement
	 * (oubli du mot de passe)
	 * @param  $login
	 * @return array
	 */
	public function identifyWithLogin($login) {

		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.identify_login', array('login' => $login)));

		$login = $GLOBALS['sql_object']->DBEscape($login);
		
		$data = $GLOBALS['sql_object']->DBSelect(SQL_get_UserInfo($login));
		if (is_array($data) && count($data) == 1) {
			return $data[0];
		} else {
			return false;
		}
	}
	
	
	/**
	 * retrieveUserProjects()
	 * Retrieve Projects for a given user
	 * @param  $a array (optional)
	 * @return array
	 */
	public function retrieveUserProjects($a = array()) {
	
		// user already logged-in
		if(isset($_SESSION['authenticated'])) {
			$login = $_SESSION['userlogin'];
			$crypt_pass = base64_decode($_SESSION['userwhois']);
		}
		else
		{
			if(isset($a['login'])) {
				$login = $a['login'];
				$crypt_pass = $a['pass'];
			} else {
				return false;
			}
		}
	
		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.retrieve_user_projects', array('login' => $login, 'password' => $crypt_pass)));
	
		$data = $GLOBALS['sql_object']->DBSelect(SQL_Get_UserProject($login, $crypt_pass));
		$projects = array();
		if(isset($data[0]['jpm_project_id'])) {
			for ($i = 0; $i < count($data); $i++) {
				array_push($projects, $data[$i]['jpm_project_id']);
			}
		}
		return $projects;
	
	}
	
	/**
	 * retrieveProjectsIndicators()
	 * Retrieve Indicators for given projects
	 * @param  $a array (optional) - containing projects ID
	 * @return array
	 */
	public function retrieveProjectsIndicators($a = array()) {
	
		if(isset($a)) {
			
			$projects = $a;
			
		} else {
			
			// user already logged-in
			if(isset($_SESSION['authenticated'])) {
				
				$projects = $_SESSION['projects'];
				
			} else {
				
				$projects = array();
				
			}
			
		}
	
		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.retrieve_user_indicators', array('data' => $projects)));
	
		$indicators = array();

		if(count($projects) > 0) {
			
			$data = $GLOBALS['sql_object']->DBSelect(SQL_Get_ProjectIndicators($projects));
			
			for ($i = 0; $i < count($data); $i++) {
				array_push($indicators, $data[$i]['jps_sdi_id']);
			}
			
		}
		return $indicators;
	
	}


	/**
	 * retrieveUserWorkgroups()
	 * Retrieve Workgroups for a given user
	 * @param  $a array (optional)
	 * @return string
	 */
	public function retrieveUserWorkgroups($a = array()) {

		// user already logged-in
		if(isset($_SESSION['authenticated'])) {
			$login = $_SESSION['userlogin'];
			$crypt_pass = base64_decode($_SESSION['userwhois']);
		}
		else
		{
			if(isset($a['login'])) {
				$login = $a['login'];
				$crypt_pass = $a['pass'];
			} else {
				return false;
			}
		}

		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.retrieve_user_workgroups', array('login' => $login, 'password' => $crypt_pass)));

		$data_w = $GLOBALS['sql_object']->DBSelect(SQL_Get_UserWorkshop($login, $crypt_pass));
		$workgroups = '';
		
		if(is_array($data_w)) {
    		for ($i = 0; $i < count($data_w); $i++) {
    			$workgroups .= "/" . $data_w[$i]['jwu_workshop_id'];
    		}
		}
		
		return $workgroups;

	}

	/**
	 * updateSessionPassword()
	 * Mise à jour du mot de passe en session (cas de changement)
	 * @param  $newpass
	 * @return boolean
	 */
	public function updateSessionPassword($newpass) {

		// Notify the beginning of the current method;
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.update_cookie_password'));

		$_SESSION['userwhois'] = base64_encode($newpass);

		return true;
	}

	/**
	 * updateCookieWorkshop()
	 * Update workgroups list in cookie
	 * used in public/ part
	 *
	 * @return void
	 */
	function updateCookieWorkshop()
	{
		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.update_cookie_workshop'));

		$str = base64_encode($this->retrieveUserWorkgroups());
		setcookie('linea21[workshop]', $str, $_COOKIE['linea21']['lifetime'], '/');
	}

	/**
	 * setUserCookie()
	 * set les infos contenu en tableau PHP en cookie
	 *
	 * @param  $array
	 * @return void
	 */
	public function setUserCookie($array)
	{
		// Notify the beginning of the current method
		// Filter data event + return value
		$r = $this->dispatcher->filter(new sfEvent(__FUNCTION__, 'auth.set_user_cookie', array('data' => $array)), $array);
		$array = $r->getReturnValue();

		$cookie_expires = $array['lifetime'];
		while (list($key, $value) = @each($array)) {
			setcookie('linea21[' . $key . ']', $value, $cookie_expires, '/');
		}
	}

	/**
	 * destroyUserCookie()
	 * Détruit les infos contenues en cookie
	 *
	 * @return void
	 */
	public function destroyUserCookie()
	{
		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.destroy_user_cookie'));

		while (list($key, $val) = @each($_COOKIE['linea21'])) {
			setcookie('linea21[' . $key . ']', '', (time() - 3600), '/');
		}
	}

	/**
	 * setCookieLastPage()
	 * used for the admin/ part
	 */
	public function setCookieLastPage() {

		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.set_cookie_lastpage'));

		if(isset($_SESSION['authenticated'])) {
			setcookie('linea21_lastpage', 'index.php?'.$_SERVER['QUERY_STRING'], (time() + 3600 * 720), '/');
			setcookie('linea21_lastuser', $_SESSION['userlogin'], (time() + 3600 * 720), '/');
			setcookie('linea21_lastactivity', time(), (time() + 3600 * 720), '/');
		}
		return true;
	}

	/**
	 * setCookieLastPage()
	 * used for the admin/ part
	 */
	public function setCookieRememberMe() {

		if(!defined('REMEMBER_ME') || REMEMBER_ME == 0) return true;
	
		// Notify the beginning of the current method
		$this->dispatcher->notify(new sfEvent(__FUNCTION__, 'auth.set_cookie_rememberme'));
	
		if(isset($_SESSION['authenticated'])) {
			$cookiehash = md5(str_shuffle($_SESSION['userwhois'] . $_SESSION['userlogin']));
			setcookie("linea21_uname", $_SESSION['userlogin'] . '_' . $cookiehash, time()+ REMEMBER_ME,'/');

			// we update the last connexion field
			$GLOBALS['sql_object']->DBQuery('UPDATE l21_user SET user_cookiehash = "' . $GLOBALS['sql_object']->DBEscape($cookiehash) . '" WHERE lower(user_login) = "' . $_SESSION['userlogin'] . '";');
		}
		return true;
	}

	/**
	 * isActive()
	 * check if last activity is less than SESS_INACTIVITY_MAXTIME
	 * If more, destroy the session
	 */
	public function isActive() {

		include_once('../admin/'.SQL.'.inc.php');

		if (!$this->isAuthenticated()) {

			// if rememberme option is disabled, we exit
			if(!defined('REMEMBER_ME') || REMEMBER_ME == 0) return false;

			// if rememberme is set
			if(isset($_COOKIE['linea21_uname'])) {

				list($login, $cookiehash) = explode ('_' , $_COOKIE['linea21_uname']);

				$data = $GLOBALS['sql_object']->DBSelect(SQL_Get_UserFromRememberMe($cookiehash, $login));

				if ($data != 0 && count($data) == 1) {

					$this->initSession($data, $login);

					return true;
				}

			} else {
				return false;
			}
		}

		if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > SESS_INACTIVITY_MAXTIME)) {
			// last request was more than SESS_INACTIVITY_MAXTIME (in seconds) ago
			session_unset();     // unset $_SESSION variable for the run-time
			session_destroy();   // destroy session data in storage
			$_SESSION = array();

			return false;
		}
		$_SESSION['LAST_ACTIVITY'] = time(); // update last activity time stamp

		return true;
	}

}

?>
