<?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
*/

class login_user extends DefaultObject
{
	protected $allowed_keys=array(
		'group_id', 'login', 'pass',  'email',  'autologin',  'last_visit',  'last_ip',	'avatar',  'icq',  'website',
		'status',  'actkey',  'realname',  'rating', 'real_id'
	);
	
	public function __construct($id=null)
	{
		$this->setGuest();
		if ($id>0) $this->loadUserDataById($id);
	}
	
	public function getAsArray()
	{
		return $this->data;
	}
	
	/**
	 * Загружает в текущий объект данные пользователя по id
	 * 
	 * @param int $id идентифитатор пользователя
	 * 
	 * @return array    массив данных пользователя
	 */
	public function loadUserDataById($id)
	{
		if ($id!=IS_GUEST) {
			if (!$cache=Cache_Generic::init()->get('user_'.$id)) {
				$data=Db::i()->selectRow(
					"SELECT *
					FROM ?_users WHERE user_id=?", $id
				);
				$this->set($data);
				$this->setAdditionalData();
				Cache_Generic::init()->set('user_'.$id, $this->data, 60*60*24*10);
			} else {
				$this->data=$cache;
			}
		} else {
			$this->setGuest();
			$this->setAdditionalData();
		}
		
		$this->setNonCachedAdditionalData();
		return $this->data;
	}
	
	/**
	 * Загружает в текущий объект данные пользователя по логину
	 * 
	 * @param string $login логин
	 * 
	 * @return array    массив данных пользователя
	 */
	public function loadUserDataByLogin($login)
	{
		if ($login) {
			$data=Db::i()->selectRow(
				"SELECT *
				FROM ?_users WHERE login=?", $login
			);
			$this->set($data);
		}
		$this->setAdditionalData();
		$this->setNonCachedAdditionalData();
		return $this->data;
	}
	
	/**
	 * Загружает в текущий объект данные пользователя по email
	 * 
	 * @param string $email
	 * 
	 * @return array    массив данных пользователя
	 */
	public function loadUserDataByEmail($email)
	{
		if ($email) {
			$data=Db::i()->selectRow(
				"SELECT *
				FROM ?_users WHERE email=?", $email
			);
			$this->set($data);
		}
		$this->setAdditionalData();
		$this->setNonCachedAdditionalData();
		return $this->data;
	}
	
	/**
	 * Загружает в текущий объект данные пользователя по openID идентификатору
	 * 
	 * @param string $open_id openID идентификатор
	 * 
	 * @return array    массив данных пользователя
	 */	
	public function loadUserDataByOpenId($open_id)
	{
		if ($open_id) {
			$data=Db::i()->selectRow(
				"SELECT *
				FROM ?_openid o JOIN ?_users u ON o.user_id=u.user_id
				WHERE openid=?", $open_id
			);
			$this->set($data);
		}
		$this->setAdditionalData();
		$this->setNonCachedAdditionalData();
		return $this->data;
	}
	
	/**
	 * Загружает дополнительные данные для пользователя
	 * 
	 * @return none    
	 */
	public function setAdditionalData()
	{
		if (!$this->data || $this->isGuest()) {
			$this->setGuest();
			return;
		}
		$this->last_ip=inet_ntop($this->last_ip);
		$this->autologin=$this->getAutologin();
	}
	
	/**
	 * Загружает некэшируемые дополнительные данные для пользователя
	 * 
	 * @return none    
	 */
	public function setNonCachedAdditionalData()
	{
		$this->online=User::isOnline($this->id);
	}
	
	public function getCurrentUser()
	{
		return $this;
	}
	
	/**
	 * добавляет нового пользователя и присваевает его данные текущему объекту
	 * 
	 * @param array $data данные пользователя
	 * 
	 * @return array    массив данных пользователя
	 */
	public function add($data)
	{
		if ($data['pass']) $data['pass']=$this->PassHash($data['pass']);
		if ($data['pass']===null) $data['pass']='';
		$data['register_date']=time();
		$data['last_ip']=inet_pton(User::getIP());
	
		if ($data['id']=Db::i()->query(
			'INSERT INTO ?_users (?#)
			VALUES(?a)',
			array_keys($data), array_values($data)
		)) {
			$data['last_ip']=inet_ntop($data['last_ip']);
			return $this->data=$data;
		}
	}
	
	/**
	 * обновляет свойства пользователя
	 * 
	 * @param array $data хэш массив параметров
	 * 
	 * @return boolean    возвращает true  в случае успеха
	 */
	public function update($data)
	{
		if ($this->isGuest()) return false;
		$this->_filterKeys($data);

		if (isset($data['pass']) && $data['pass']) {
			$data['pass']=$this->PassHash($data['pass']);
			$this->setNewAutologin();
		}
		
		if ($this->_updateDB($data)) {
			$this->set($data);
			$this->_resetItemCache();
			return true;
		}
	}
	
	protected function _updateDB(&$data)
	{
		return Db::i()->query(
			"UPDATE ?_users SET  ?a WHERE `user_id` = ?", $data, $this->id
		);
	}
	
	protected function _resetItemCache()
	{
		$this->resetCache();
	}
	
	public function resetCache($ids=null)
	{
		if (null==$ids) $ids=array($this->id);
		elseif(!is_array($ids)) $ids=array($ids);
		foreach ($ids as $k=>$v) {
			$ids[$k]='user_'.$v;
		}
		Cache_Generic::init()->remove($ids);
	}
	
	public function delete()
	{
		$this->update(array('status'=>-1));
	}
	
	/**
	 * проверяет является ли пользователь гостем
	 * 
	 * @return boolean    результат проверки
	 */
	public function isGuest()
	{
		return $this->id==IS_GUEST;
	}

	/**
	 * проверяет является ли пользователь залогиненным пользователем
	 * 
	 * @return boolean    результат проверки
	 */
	public function isNotGuest()
	{
		return $this->id>0;
	}
	
	/**
	 * проверяет не является ли аккаунт удаленным
	 * 
	 * @return boolean    результат проверки
	 */
	public function isDeleted()
	{
		return $this->status==-1;
	}
	
	/**
	 * проверяет является ли аккаунт активированным
	 * 
	 * @return boolean    результат проверки
	 */
	public function isActivated()
	{
		return $this->status>0;
	}
	
	/**
	 * проверяет является ли пользователь суперадмином
	 * 
	 * @return boolean    результат проверки
	 */
	public function isSuperАdmin()
	{
		return $this->group_id==1;
	}
	
	public function isActiveUser()
	{
		return ($this->isNotGuest() && !$this->isDeleted() && $this->isActivated());
	}
	
	/**
	 * Сравнивает пароль с хранящимся в базе
	 * 
	 * @param string $pass пароль
	 * 
	 * @return boolean    результат проверки
	 */
	public function isPasswordCorrect($pass)
	{
		//если используется новый алгоритм хэширования
		if (substr($this->pass, 0, 1)=='$') {
			return ($this->PassHash($pass, $this->pass)==$this->pass);
		} elseif ($this->pass) {//если вообще имеется хэш
			//если совпадает MD5
			if ($this->OldPassHash($pass)==$this->pass) {
				//обновляем хэш на современный
				$this->update(array('pass'=>$pass));
				return true;
			}
		}
		return false;
	}
	
	/**
	 * получение хэша пароля с солью и перцем
	 * @param string	пароль для хэширования
	 * @param string	соль, если проверяем совпадение пароля
	 * @return string	хэш для помещения в БД
	 * @author Loki
	 */
	public function PassHash($pass, $salt=false)
	{
		//SECURECODE - это "перец"
		$pass=sha1($pass.SECURECODE);
		if (!$salt) {
			//если доступен blowfish
			if (CRYPT_BLOWFISH == 1) {
				$salt='';
				//если версия < 5.3.7 то 2a
				if (version_compare(PHP_VERSION, '5.3.7', '<')) {
					$salt.='$2a$10$';
				} else {//если версия >=5.3.7 то 2y
					$salt.='$2y$10$';
				}
				$salt.=$this->_getSalt(22);
			} else {
				//в противном случае используем MD5 $1$
				$salt='$1$'.$this->_getSalt(9);
			}
		}
		return crypt($pass, $salt);
	}
	
	protected function _getSalt($length)
	{
		$salt='';
		$salt_chars = array_merge(range('A', 'Z'), range('a', 'z'), range(0, 9));
		for ($i=0; $i < $length; $i++) {
			$salt .= $salt_chars[array_rand($salt_chars)];
		}
		return $salt;
	}
	
	/**
	 * двойное хэширование пароля с секретным ключом SECURECODE
	 * @param string	пароль для хэширования
	 * @param boolean	ключ указывающий что пароль уже один раз хэширован (например, взят из кук)
	 * @return string	хэш для помещения в БД
	 * @author Loki
	 */
	public function OldPassHash($pass, $hashed_once=false)
	{
		if ($hashed_once) $hash=$this->PassHashOnce($pass);
		else $hash=$this->PassHashOnce($this->PassHashOnce($pass));
		return $hash;
	}
	
	/**
	 * хэширование пароля с секретным ключом SECURECODE
	 * @param string	пароль для хэширования
	 * @return string	хэш
	 */	
	public function PassHashOnce($pass)
	{
		return md5($pass.SECURECODE);
	}
	
	/**
	 * добавляет в базу новый идентификатор openID для текущего пользователя
	 * 
	 * @param string $open_id openID идентификатор
	 * 
	 * @return none 
	 */
	public function addOpenId($open_id)
	{
		return Db::i()->query(
			"INSERT IGNORE INTO ?_openid (user_id, openid) VALUES(?, ?)",
			$this->id, $open_id
		);
	}
	
	/**
	 * удаляет из базы указанный идентификатор openID для текущего пользователя
	 * 
	 * @param string $open_id openID идентификатор
	 * 
	 * @return none 
	 */
	public function deleteOpenID($openid)
	{
		if (!is_array($openid)) $openid=array($openid);
		if (count($openid)) {
			Db::i()->query(
				"DELETE FROM ?_openid WHERE user_id=? AND id IN (?a)",
				$this->id, $openid
			);
		}
	}
	
	/**
	 * возвращает идентификатор для автологина
	 * 
	 * @return string    идентификатор
	 */
	public function getAutologin()
	{
		if ($this->autologin) return $this->autologin;
		return $this->setNewAutologin();
	}
	
	/**
	 * генерирует новый ключ для автологина
	 * 
	 * @return string    ключ автологина
	 */
	public function setNewAutologin()
	{
		$this->autologin=md5($this->pass.rand().microtime());
		$this->update(array('autologin'=>$this->autologin));
		return $this->autologin;
	}
	
	/**
	 * устанавливает куку автологина
	 * 
	 * @return none    
	 */
	public function SetCookieAuth()
	{
		@setcookie(
			'autologin',
			$this->id.'-'.$this->autologin,
			time()+60*60*24*365,
			"/",
			LabCMS::getCookieDomain()
		);
	}
	
	/**
	 * удаляет куку автологина
	 * 
	 * @return none    
	 */
	public function DeleteCookieAuth()
	{
		@setcookie('autologin', '', 0, "/", LabCMS::getCookieDomain());
	}
	
	public function setGuest()
	{
		$this->data=array('id'=>IS_GUEST, 'group_id'=>IS_GUEST);
		$this->data['user_id']=&$this->data['id'];
	}
	
	/**
	 * получает массив пользователей по списку id
	 * @param array список id пользователей
	 * 
	 * @return array массив пользователей вида user_id=>array(...)    
	 */
	public function getUsersByIds($ids)
	{
		if (!$ids) return;
		
		$ids=array_unique($ids);
		$users=array();
		
		//TODO генерацию ключей надо будет вынести в отдельное место
		$keys=array();
		foreach ($ids as $id) {
			if ($id) $keys[$id]='user_'.$id;
		}
		$empty_users=array();

		if ($cache_users=Cache_Generic::init()->get(array_values($keys))) {
			foreach ($keys as $id=>$key) {
				if (array_key_exists($key, $cache_users) && is_array($cache_users[$key])) {
					$users[$id]=User::getUser();
					$users[$id]->set($cache_users[$key]);
					$users[$id]->setNonCachedAdditionalData();
				} else {
					$empty_users[$id]=$key;
				}
			}
		} else {
			$empty_users=$keys;
		}

		if ($empty_users) {
			$data=Db::i()->select(
				"SELECT *
				FROM ?_users
				WHERE user_id IN (?a)", array_keys($empty_users)
			);
			if ($data) {
				foreach ($data as $user) {
					$id=$user['user_id'];
					$users[$id]=User::getUser();
					$users[$id]->set($user);
					$users[$id]->setAdditionalData();
					Cache_Generic::init()->set($keys[$users[$id]->id], $users[$id]->getAsArray(), 60*60*24*10);
					$users[$id]->setNonCachedAdditionalData();
				}
			}
		}

		return $users;
	}
}