<?php
/*
* @package LabCMS
* @copyright (c) 2006 Alexander Levin aka Loki (loki_angel@mail.ru)
* @support http://labcms.ru
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/

//группа незарегистрированных пользователей
define('IS_GUEST', -1);

class User
{
	private static $instance;
	private $last_reset=0;
	public $info;
	public $group_id=IS_GUEST;
	public $id=IS_GUEST;
	private $obj;
	private static $parent_groups=array();
	/**
	 * конструктор. при инициализации класса если данные уже имеются в сессии, то берет оттуда
	 * если же нет, то пытается авторизовать через куки. если авторизация неудачна, либо сессия украдена
	 * то считает посетителя гостем
	 * @return none
	 * @author Loki
	 */
	function __construct()
	{
		//проверяем сессию и берем данные из сессии, если они там есть
		$this->_checkSessionValid();
		$this->prepareClass();

		//производим проверку принудительного сброса сессии
		$this->_resetSession();
		if ($this->isGuest()) $this->_autologin();
	}
	
	public function setData()
	{
		$this->_FetchUserInfo();
		$this->_updateLastRequest();
		if (rand(0, 100)==50) $this->_cleanOnliners();
	}
	
	private function __clone() {}
	
	public static function i()
	{
		return self::instance();
	}

	public static function instance()
	{
		if (self::$instance === null) {
			self::$instance = new self;
		}
		return self::$instance;
	}
	
	private function prepareClass()
	{
		if (!LabConfig::$cfg['auth_module']) LabConfig::$cfg['auth_module']='login';
		include_once LAB_PATH_MODULES.'/'.LabConfig::$cfg['auth_module'].'/lc.auth.class.php';
		$classname=LabConfig::$cfg['auth_module'].'_user';
		$this->obj=new $classname($this->id, $this->group_id);
	}
	
	private function _autologin()
	{

		return $this->obj->autologin();
	}
	
	public function auth($data=array())
	{
		$this->obj->auth($data);
		$this->_FetchUserInfo();
		return $this->id>0;
	}
	
	public function getUserId($data)
	{
		return $this->obj->getUserId($data);
	}
	
	private function _checkSessionValid()
	{
		/*******************
		$this->id=$_SESSION['user_id'];
		$this->group_id=$_SESSION['group_id'];
		return;
		/********************/
		//если пользователь залогинен, берем базовые данные из сессии
		if(!empty($_SESSION['user_id']) && !empty($_SESSION['group_id']) && $_SESSION['hash_key']==$this->_getHashKey())
		{
			$this->id=$_SESSION['user_id'];
			$this->group_id=$_SESSION['group_id'];
		}
		//если пользователь не залогинен, то считаем его гостем
		else
		{
			$this->id=$this->group_id=$_SESSION['user_id']=$_SESSION['group_id']=IS_GUEST;
			$_SESSION['hash_key']=$this->_getHashKey();
		}
	}
	
	private function _getHashKey()
	{
		switch(LabConfig::$cfg['session_key'])
		{
			case('ip+user_agent'):
				return md5($_SERVER['REMOTE_ADDR'].$_SERVER["HTTP_USER_AGENT"]);
			break;
			case('ip'):
				return md5($_SERVER['REMOTE_ADDR']);
			break;
			default;
				return false;
		}
	}
	
	private function _FetchUserInfo()
	{
		$this->info=$this->getUserInfo($this->id);
		if (empty($this->info['token'])) $this->info['token']=$this->_getToken();
		if ($this->info) foreach($this->info as $prop=>$val)
		{
			$this->$prop=$val;
		}
		$_SESSION['user_id']=$this->id;
		$_SESSION['group_id']=$this->group_id;
		$_SESSION['user_info']=$this->info;
	}
	
	/**
	 * получение информации о пользователе
	 * @param integer	id пользователя
	 * @return array	ассоциированный массив с данными пользователя. иначе false
	 * @author Loki
	 */	
	function getUserInfo($user_id)
	{
		if ($user_info=$this->obj->getUserInfo($user_id))
		{
			if (!$user_info['groups']) $user_info['groups']=self::getParentGroups($user_info['group_id']);
			return $user_info;
		}
		else return false;
	}
	
	/**
	 * Разлогинивание.
	 * @author Loki
	 */
	function logout()
	{
		$_SESSION['user_id']=$_SESSION['group_id']=IS_GUEST;
		$_SESSION['user_info']=null;
		$this->obj->logout();
	}
	
	/**
	 * метод выбирает все родительские группы для заданной  и помещает их в массив
	 * @param integer Идентификатор группы
	 * @return array Все группы, являющиеся родителями запрошенной
	 * @author Loki
	 */
	static function getParentGroups($group_id)
	{
		if ($group_id==IS_GUEST) return array();

		if(isset(self::$parent_groups[$group_id])) return self::$parent_groups[$group_id];
		
		$target_id=$group_id;
		$all_groups=self::_getAllUserGroups();
		while($all_groups[$group_id])
		{
			$groups[]=$group_id=$all_groups[$group_id];
		}
		return self::$parent_groups[$target_id]=array_reverse($groups);
	}
	
	/**
	 * функция устанавливает дату последнего обновления параметров
	 *
	 * @param none
	 * @return none
	 * @author Loki
	 */
	public static function setResetSession()
	{
		$new_time = time();
		file_put_contents(LAB_PATH_DATA.'/session_reset.php',"<?\r\n\$this->last_reset=".time().";");
	}

	public function ForceResetSession()
	{
		$_SESSION['user_info']=null;
		$_SESSION['group_id']=$this->group_id=$this->obj->getGroupByUserId($this->id);
		$_SESSION['last_reset']=$this->last_reset;
	}
	
	/**
	 * функция проверяет должен ли быть сброс сессии и производит его, если нужно
	 *
	 * @param none
	 * @return none
	 * @author Loki
	 */
	private function _resetSession()
	{
		$this->_getLastReset();
		if (!isset($_SESSION['last_reset']) || $_SESSION['last_reset']!=$this->last_reset)
		{
			$this->ForceResetSession();
		}
	}
	
	private function _getLastReset()
	{
		if (file_exists(LAB_PATH_DATA.'/session_reset.php'))
			include LAB_PATH_DATA.'/session_reset.php';
	}
	
	private static function _getAllUserGroups()
	{
		return Db::i()->selectCol("-- CACHE: 24h
										SELECT parent_id, group_id AS ARRAY_KEY FROM ?_groups");
	}
	
	private function _updateLastRequest()
	{
		if ($this->id>0 && $_SESSION['user_info']['request_time']<(time()-60))
		{
			$_SESSION['user_info']['request_time']=time();
			Db::i()->query("INSERT INTO ?_users_online (user_id, request_time) VALUES(?d, NOW()) ON DUPLICATE KEY UPDATE request_time=NOW()", $this->id);
		}
	}
	
	public function isOnline($user_id)
	{
		if (!isset($this->onliners)) $this->onliners=$this->getOnliners();
		return (empty($this->onliners[$user_id])?0:$this->onliners[$user_id]);
	}
	
	function getOnliners()
	{
		if (LabConfig::$cfg['online_time']) $maxlifetime=LabConfig::$cfg['online_time']*60;
		else $maxlifetime=ini_get('session.gc_maxlifetime');
		return Db::i()->selectCol("-- CACHE: 60
								SELECT user_id AS ARRAY_KEY, request_time FROM ?_users_online WHERE request_time>=NOW() - INTERVAL ?d SECOND", $maxlifetime);
	}
	
	private function _cleanOnliners()
	{
		if (!empty(LabConfig::$cfg['online_time'])) $maxlifetime=LabConfig::$cfg['online_time']*60;
		else $maxlifetime=ini_get('session.gc_maxlifetime');
		$this->obj->saveLastRequests($maxlifetime);
		Db::i()->query("DELETE FROM ?_users_online WHERE request_time<NOW() - INTERVAL ?d SECOND", $maxlifetime);
	}
	
	private function _getToken()
	{
		if ($_SESSION['token']) return $_SESSION['token'];
		if ($this->id>0)
			$_SESSION['token']=md5($this->pass.md5(SECURECODE.date("Ymd", time()-18000)));
		else
			$_SESSION['token']=md5(rand().rand());
		return $_SESSION['token'];
	}
	
	public static function checkToken($token=null, $break=true)
	{
		if (!$token) $token=isset($_REQUEST['token'])?$_REQUEST['token']:null;
	
		if (!empty($_SESSION['token']) && $_SESSION['token']==$token) return true;
		if ($break) die('Ошибка сессии');
		return LabCMS::i()->error('Ошибка сессии');
	}
	
	public function isLoginExists($login)
	{
		return $this->obj->isLoginExists($login);
	}
	
	/**
	 * двойное хэширование пароля с секретным ключом SECURECODE
	 * @param string	пароль для хэширования
	 * @param boolean	ключ указывающий что пароль уже один раз хэширован (например, взят из кук)
	 * @return string	хэш для помещения в БД
	 * @author Loki
	 */
	static function PassHash($pass, $hashed_once=false)
	{
		if ($hashed_once) $hash=self::PassHashOnce($pass);
		else $hash=self::PassHashOnce(self::PassHashOnce($pass));
		return $hash;
	}
	
	/**
	 * хэширование пароля с секретным ключом SECURECODE
	 * @param string	пароль для хэширования
	 * @return string	хэш
	 * @author Loki
	 */	
	static function PassHashOnce($pass)
	{
		return md5($pass.SECURECODE);
	}
	
	//возвращает ip текущего пользователя
	public static function getIP()
	{
		return $_SERVER['REMOTE_ADDR'];
	}
	
	public function isGuest()
	{
		return $this->id==IS_GUEST;
	}
	
	private function _setGuest()
	{
		$this->id=$this->group_id=IS_GUEST;
	}
}