<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

require_once '../builder/modules/auth/libraries/phpass-0.3/PasswordHash.php';

define('STATUS_ACTIVATED', '1');
define('STATUS_NOT_ACTIVATED', '0');

/**
 * Tank_auth
 *
 * Authentication library for Code Igniter.
 *
 * @package		Tank_auth
 * @author		Ilya Konyukhov (http://konyukhov.com/soft/)
 * @version		1.0.9
 * @based on	DX Auth by Dexcell (http://dexcell.shinsengumiteam.com/dx_auth)
 * @license		MIT License Copyright (c) 2008 Erick Hartanto
 */
class Tank_auth
{
	private $error = array();
	private $CI;
	
	function __construct()
	{
		$this->CI =& get_instance();

		$this->CI->load->config('auth/tank_auth', TRUE);
		$this->CI->load->model('auth/users_model','users');

		// Try to autologin
		$this->autologin();
	}

	/**
	 * Login user on the site. Return TRUE if login is successful
	 * (user exists and activated, password is correct), otherwise FALSE.
	 *
	 * @param	string	(username or email or both depending on settings in config file)
	 * @param	string
	 * @param	bool
	 * @return	bool
	 */
    function login($login, $password, $remember, $login_by_username, $login_by_email, $user_conected, $externo = false)
	{
	    if($externo == 'facebook'){
	        $user = $this->CI->users->get_user_by_email($login);
	        return $this->generate_login_session($login,$user,$remember);
	    }
	    
		if ((strlen($login) > 0) AND (strlen($password) > 0)) {

			// Which function to use to login (based on config)
			if ($login_by_username AND $login_by_email) {
				$get_user_func = 'get_user_by_login';
			} else if ($login_by_username) {
				$get_user_func = 'get_user_by_username';
			} else {
				$get_user_func = 'get_user_by_email';
			}

			if (!is_null($user = $this->CI->users->$get_user_func($login))) {	// login ok

				// Does password match hash in database?
				$hasher = new PasswordHash(
					$this->CI->config->item('phpass_hash_strength', 'tank_auth'),
					$this->CI->config->item('phpass_hash_portable', 'tank_auth')
		        );
				if ($hasher->CheckPassword($password, $user->password)) // password ok
				{					
					if ($user->banned == 1) // fail - banned
					{									
						$this->error = array('banned' => $user->ban_reason);
					}
					elseif ($user_conected === TRUE)
					{
						$this->CI->session->set_userdata('new_login',$login);
						
						$error = "Este usuário já está conectado, para encerrar a outra sessão digite seus dados de acesso novamente e faça o login.";
						
						$this->error = array('user_conected' => $error);
					}
					else 
					{		
						$this->disconnect_conected_user($login);
						
						if(empty($this->error))
						{
							if($this->generate_login_session($login,$user,$remember) === TRUE)
							{
								return TRUE;
							}
						}
					}
				} 
				else // fail - wrong password
				{														
					$this->increase_login_attempt($login);
					$this->error = array('password' => 'auth_incorrect_password');
				}
			} 
			else // fail - wrong login
			{															
				$this->increase_login_attempt($login);
				$this->error = array('login' => 'auth_incorrect_login');
			}
		}
		return FALSE;
	}
	
	private function disconnect_conected_user($login)
	{
		$new_login = $this->CI->session->userdata('new_login');
		
		if ($new_login == $login)
		{
			$deleted = $this->CI->users->delete_user_by_login($login);
			if($deleted === FALSE)
			{				
				$this->error = array('disconnect' => 'Erro ao encerrar outra sessão.');
			}
		}	
	}
	
	private function generate_login_session($login,$user,$remember)
	{
		$dh = explode(" ",$user->last_login);
		$dt = converte_data($dh[0],2);
		$hr = $dh[1];
		
		$session = array(
				'user_id'         => $user->id,
				'id_cadastro'     => $user->id_cadastro,
				'perfil'          => $user->perfil,
				'nome'		      => $user->nome,
				'username'	      => $user->username,
				'secret_google'   => $user->google_secret_key,
				'email'	          => $user->email,
				'data_acesso'     => $dt,
				'hora_acesso'     => $hr,
				'n_acessos_sistema' => ($user->acessos_sistema)+1,
				'ip_atual'        => ip(),
				'status'	      => ($user->activated == 1) ? STATUS_ACTIVATED : STATUS_NOT_ACTIVATED
		);
		
		$this->CI->session->set_userdata($session);
		
		log_message('info','sess_use_database: '.json_encode($this->CI->config->item('sess_use_database')));
		
		$this->CI->users->set_user_id_session(session_id(),$user->id);
		
		if ($user->activated == 0) // fail - not activated
		{
			$this->error = array('not_activated' => '');
		}
		else
		{
			if ($remember == 1)
			{
				$this->create_autologin($user->id);
			}
		
			$this->clear_login_attempts($login);
		
			$this->CI->users->update_login_info(
					$user->id,
					$this->CI->config->item('login_record_ip', 'tank_auth'),
					$this->CI->config->item('login_record_time', 'tank_auth'),
					$user->acessos_sistema
					);
				
			$this->CI->users->insert_new_login(
					$user->username,
					date('Y-m-d H:i:s'),
					ip(),
					$user->perfil
					);
			return TRUE;
		}
		return FALSE;
	}

	/**
	 * Logout user from the site
	 *
	 * @return	void
	 */
	function logout()
	{
	    $modal_account = $this->CI->session->flashdata('account_activated');
	    $this->delete_autologin();

		// See http://codeigniter.com/forums/viewreply/662369/ as the reason for the next line
		$this->CI->session->set_userdata(array('user_id' => '', 'username' => '', 'status' => ''));
		$this->CI->session->sess_destroy();
		
		if($modal_account !== null){
		    if($modal_account === true)
		        redirect('auth/redireciona/1');
		  redirect('auth/redireciona/0');
		}
		redirect('painel');
	}
	
	
	/**
	 * valida sessão se logado
	 *
	 * @return void
	 */
	function valida_sesssao()
	{
		if($this->is_logged_in() == FALSE){
		    redirect('painel');
		}
	}	
	
	/**
	 * Check if user logged in. Also test if user is activated or not.
	 *
	 * @param	bool
	 * @return	bool
	 */
	function is_logged_in($activated = TRUE)
	{	    		
		return $this->CI->session->userdata('status') === ($activated ? STATUS_ACTIVATED : STATUS_NOT_ACTIVATED);
	}
	
	/**
	 * Check if user is alreafy logged in all table ci session
	 *
	 * @param	bool
	 * @return	bool
	 */
	function in_ci_sessions($login)
	{
	    return $this->CI->users->search_login_in_ci_session($login);
	}

	/**
	 * Get user_id
	 *
	 * @return	string
	 */
	function get_user_id()
	{
		return $this->CI->session->userdata('user_id');
	}

	/**
	 * Get username
	 *
	 * @return	string
	 */
	function get_username()
	{
		return $this->CI->session->userdata('username');
	}

	/**
	 * Create new user on the site and return some data about it:
	 * user_id, username, password, email, new_email_key (if any).
	 *
	 * @param	string
	 * @param	string
	 * @param	string
	 * @param	bool
	 * @return	array
	 */
	function create_user($username, $email, $password, $email_activation)
	{
		if ((strlen($username) > 0) AND !$this->CI->users->is_username_available($username)) {
			$this->error = array('username' => 'auth_username_in_use');

		} elseif (!$this->CI->users->is_email_available($email)) {
			$this->error = array('email' => 'auth_email_in_use');

		} else {
			// Hash password using phpass
			$hasher = new PasswordHash(
					$this->CI->config->item('phpass_hash_strength', 'tank_auth'),
					$this->CI->config->item('phpass_hash_portable', 'tank_auth'));
			$hashed_password = $hasher->HashPassword($password);

			$data = array(
				'username'	=> $username,
				'password'	=> $hashed_password,
				'email'		=> $email,
				'last_ip'	=> $this->CI->input->ip_address(),
			);

			if ($email_activation) {
				$data['new_email_key'] = md5(rand().microtime());
			}
			if (!is_null($res = $this->CI->users->create_user($data, !$email_activation))) {
				$data['user_id'] = $res['user_id'];
				$data['password'] = $password;
				unset($data['last_ip']);
				return $data;
			}
		}
		return NULL;
	}

	/**
	 * Check if username available for registering.
	 * Can be called for instant form validation.
	 *
	 * @param	string
	 * @return	bool
	 */
	function is_username_available($username)
	{
		return ((strlen($username) > 0) AND $this->CI->users->is_username_available($username));
	}

	/**
	 * Check if email available for registering.
	 * Can be called for instant form validation.
	 *
	 * @param	string
	 * @return	bool
	 */
	function is_email_available($email)
	{
		return ((strlen($email) > 0) AND $this->CI->users->is_email_available($email));
	}

	/**
	 * Change email for activation and return some data about user:
	 * user_id, username, email, new_email_key.
	 * Can be called for not activated users only.
	 *
	 * @param	string
	 * @return	array
	 */
	function change_email($email)
	{
		$user_id = $this->CI->session->userdata('user_id');

		if (!is_null($user = $this->CI->users->get_user_by_id($user_id, FALSE))) {

			$data = array(
				'user_id'	=> $user_id,
				'username'	=> $user->username,
				'email'		=> $email,
			);
			if (strtolower($user->email_1) == strtolower($email)) {		// leave activation key as is
				$data['new_email_key'] = $user->new_email_key;
				return $data;

			} elseif ($this->CI->users->is_email_available($email)) {
				$data['new_email_key'] = md5(rand().microtime());
				$this->CI->users->set_new_email($user_id, $email, $data['new_email_key'], FALSE);
				return $data;

			} else {
				$this->error = array('email' => 'auth_email_in_use');
			}
		}
		return NULL;
	}

	/**
	 * Activate user using given key
	 *
	 * @param	string
	 * @param	string
	 * @param	bool
	 * @return	bool
	 */
	function activate_user($user_id, $activation_key, $activate_by_email = TRUE)
	{
		$this->CI->users->purge_na($this->CI->config->item('email_activation_expire', 'tank_auth'));

		if ((strlen($user_id) > 0) AND (strlen($activation_key) > 0)) {
			return $this->CI->users->activate_user($user_id, $activation_key, $activate_by_email);
		}
		return FALSE;
	}

	/**
	 * Set new password key for user and return some data about user:
	 * user_id, username, email, new_pass_key.
	 * The password key can be used to verify user when resetting his/her password.
	 *
	 * @param	string
	 * @return	array
	 */
	function forgot_password($login)
	{
		if (strlen($login) > 0) {
			if (!is_null($user = $this->CI->users->get_user_by_login($login))) {

				$data = array(
					'user_id'		=> $user->id,
					'username'		=> $user->username,
					'email'			=> $user->email_1,
					'new_pass_key'	=> md5(rand().microtime()),
				);

				$this->CI->users->set_password_key($user->id, $data['new_pass_key']);
				return $data;

			} else {
				$this->error = array('login' => 'auth_incorrect_email_or_username');
			}
		}
		return NULL;
	}

	/**
	 * Check if given password key is valid and user is authenticated.
	 *
	 * @param	string
	 * @param	string
	 * @return	bool
	 */
	function can_reset_password($user_id, $new_pass_key)
	{
		if ((strlen($user_id) > 0) AND (strlen($new_pass_key) > 0)) {
			return $this->CI->users->can_reset_password(
				$user_id,
				$new_pass_key,
				$this->CI->config->item('forgot_password_expire', 'tank_auth'));
		}
		return FALSE;
	}

	/**
	 * Replace user password (forgotten) with a new one (set by user)
	 * and return some data about it: user_id, username, new_password, email.
	 *
	 * @param	string
	 * @param	string
	 * @return	bool
	 */
	function reset_password($user_id, $new_pass_key, $new_password)
	{
		if ((strlen($user_id) > 0) AND (strlen($new_pass_key) > 0) AND (strlen($new_password) > 0)) {

			if (!is_null($user = $this->CI->users->get_user_by_id($user_id, TRUE))) {

				// Hash password using phpass
				$hasher = new PasswordHash(
						$this->CI->config->item('phpass_hash_strength', 'tank_auth'),
						$this->CI->config->item('phpass_hash_portable', 'tank_auth'));
				$hashed_password = $hasher->HashPassword($new_password);

				if ($this->CI->users->reset_password(
						$user_id,
						$hashed_password,
						$new_pass_key,
						$this->CI->config->item('forgot_password_expire', 'tank_auth'))) {	// success

					// Clear all user's autologins
					$this->CI->load->model('auth/user_autologin');
					$this->CI->user_autologin->clear($user->id);

					return array(
						'user_id'		=> $user_id,
						'username'		=> $user->username,
						'email'			=> $user->email_1,
						'new_password'	=> $new_password,
					);
				}
			}
		}
		return NULL;
	}

	/**
	 * Change user password (only when user is logged in)
	 *
	 * @param	string
	 * @param	string
	 * @return	bool
	 */
	function change_password($old_pass, $new_pass)
	{
		$user_id = $this->CI->session->userdata('user_id');

		if (!is_null($user = $this->CI->users->get_user_by_id($user_id, TRUE))) {

			// Check if old password correct
			$hasher = new PasswordHash(
					$this->CI->config->item('phpass_hash_strength', 'tank_auth'),
					$this->CI->config->item('phpass_hash_portable', 'tank_auth'));
			if ($hasher->CheckPassword($old_pass, $user->password)) {			// success

				// Hash new password using phpass
				$hashed_password = $hasher->HashPassword($new_pass);

				// Replace old password with new one
				$this->CI->users->change_password($user_id, $hashed_password);
				return TRUE;

			} else {															// fail
				$this->error = array('old_password' => 'auth_incorrect_password');
			}
		}
		return FALSE;
	}

	/**
	 * Change user email (only when user is logged in) and return some data about user:
	 * user_id, username, new_email, new_email_key.
	 * The new email cannot be used for login or notification before it is activated.
	 *
	 * @param	string
	 * @param	string
	 * @return	array
	 */
	function set_new_email($new_email, $password)
	{
		$user_id = $this->CI->session->userdata('user_id');

		if (!is_null($user = $this->CI->users->get_user_by_id($user_id, TRUE))) {

			// Check if password correct
			$hasher = new PasswordHash(
					$this->CI->config->item('phpass_hash_strength', 'tank_auth'),
					$this->CI->config->item('phpass_hash_portable', 'tank_auth'));
			if ($hasher->CheckPassword($password, $user->password)) {			// success

				$data = array(
					'user_id'	=> $user_id,
					'username'	=> $user->username,
					'new_email'	=> $new_email,
				);

				if ($user->email_1 == $new_email) {
					$this->error = array('email' => 'auth_current_email');

				} elseif ($user->new_email == $new_email) {		// leave email key as is
					$data['new_email_key'] = $user->new_email_key;
					return $data;

				} elseif ($this->CI->users->is_email_available($new_email)) {
					$data['new_email_key'] = md5(rand().microtime());
					$this->CI->users->set_new_email($user_id, $new_email, $data['new_email_key'], TRUE);
					return $data;

				} else {
					$this->error = array('email' => 'auth_email_in_use');
				}
			} else {															// fail
				$this->error = array('password' => 'auth_incorrect_password');
			}
		}
		return NULL;
	}

	/**
	 * Activate new email, if email activation key is valid.
	 *
	 * @param	string
	 * @param	string
	 * @return	bool
	 */
	function activate_new_email($user_id, $new_email_key)
	{
		if ((strlen($user_id) > 0) AND (strlen($new_email_key) > 0)) {
			return $this->CI->users->activate_new_email(
					$user_id,
					$new_email_key);
		}
		return FALSE;
	}

	/**
	 * Delete user from the site (only when user is logged in)
	 *
	 * @param	string
	 * @return	bool
	 */
	function delete_user($password)
	{
		$user_id = $this->CI->session->userdata('user_id');

		if (!is_null($user = $this->CI->users->get_user_by_id($user_id, TRUE))) {

			// Check if password correct
			$hasher = new PasswordHash(
					$this->CI->config->item('phpass_hash_strength', 'tank_auth'),
					$this->CI->config->item('phpass_hash_portable', 'tank_auth'));
			if ($hasher->CheckPassword($password, $user->password)) {			// success

				$this->CI->users->delete_user($user_id);
				$this->logout();
				return TRUE;

			} else {															// fail
				$this->error = array('password' => 'auth_incorrect_password');
			}
		}
		return FALSE;
	}

	/**
	 * Get error message.
	 * Can be invoked after any failed operation such as login or register.
	 *
	 * @return	string
	 */
	function get_error_message()
	{
		return $this->error;
	}

	/**
	 * Save data for user's autologin
	 *
	 * @param	int
	 * @return	bool
	 */
	private function create_autologin($user_id)
	{
		$this->CI->load->helper('cookie');
		$key = substr(md5(uniqid(rand().get_cookie($this->CI->config->item('sess_cookie_name')))), 0, 16);

		$this->CI->load->model('auth/user_autologin');
		$this->CI->user_autologin->purge($user_id);

		if ($this->CI->user_autologin->set($user_id, md5($key))) {
			set_cookie(array(
					'name' 		=> $this->CI->config->item('autologin_cookie_name', 'tank_auth'),
					'value'		=> serialize(array('user_id' => $user_id, 'key' => $key)),
					'expire'	=> $this->CI->config->item('autologin_cookie_life', 'tank_auth'),
			));
			return TRUE;
		}
		return FALSE;
	}

	/**
	 * Clear user's autologin data
	 *
	 * @return	void
	 */
	private function delete_autologin()
	{
		$this->CI->load->helper('cookie');
		if ($cookie = get_cookie($this->CI->config->item('autologin_cookie_name', 'tank_auth'), TRUE)) {

			$data = unserialize($cookie);

			$this->CI->load->model('auth/user_autologin');
			$this->CI->user_autologin->delete($data['user_id'], md5($data['key']));

			delete_cookie($this->CI->config->item('autologin_cookie_name', 'tank_auth'));
		}
	}

	/**
	 * Login user automatically if he/she provides correct autologin verification
	 *
	 * @return	void
	 */
	private function autologin()
	{	    
		if (!$this->is_logged_in() AND !$this->is_logged_in(FALSE)) {			// not logged in (as any user)
		    
			$this->CI->load->helper('cookie');
			if ($cookie = get_cookie($this->CI->config->item('autologin_cookie_name', 'tank_auth'), TRUE)) {

				$data = unserialize($cookie);

				if (isset($data['key']) AND isset($data['user_id'])) {

					$this->CI->load->model('auth/user_autologin');
					if (!is_null($user = $this->CI->user_autologin->get($data['user_id'], md5($data['key'])))) {
					    
						$this->CI->load->helper('funcoes');
						$this->CI->load->model('sistema/usuario_model');
						$last_login = $this->CI->usuario_model->consulta_ultimo_login($user->username);
						
						$dh = explode(" ",$last_login->datahora);
						$dt = converte_data($dh[0],2);
						$hr = $dh[1];
						
						// Login user
						$this->CI->session->set_userdata(array(			
								'user_id'         => $user->id,
                				'id_cadastro'     => $user->id_cadastro,
                				'perfil'          => $user->perfil,
                				'nome'		      => $user->nome,
                				'username'	      => $user->username,
                				'email'	          => $user->email,
                				'data_acesso'     => $dt,
                				'hora_acesso'     => $hr,
                				'n_acessos_sistema' => ($user->acessos_sistema)+1,
                				'ip_atual'        => ip(),
                				'status'	      => ($user->activated == 1) ? STATUS_ACTIVATED : STATUS_NOT_ACTIVATED
						));
						
						$this->CI->users->set_user_id_session(session_id(),$user->id);
						
						// Renew users cookie to prevent it from expiring
						set_cookie(array(
								'name' 		=> $this->CI->config->item('autologin_cookie_name', 'tank_auth'),
								'value'		=> $cookie,
								'expire'	=> $this->CI->config->item('autologin_cookie_life', 'tank_auth'),
						));

						$this->CI->users->update_login_info(
								$user->id,
								$this->CI->config->item('login_record_ip', 'tank_auth'),
								$this->CI->config->item('login_record_time', 'tank_auth'));
						return TRUE;
					}
				}
			}
		}
		return FALSE;
	}

	/**
	 * Check if login attempts exceeded max login attempts (specified in config)
	 *
	 * @param	string
	 * @return	bool
	 */
	function is_max_login_attempts_exceeded($login)
	{	    
		if ($this->CI->config->item('login_count_attempts', 'tank_auth')) {
			$this->CI->load->model('auth/login_attempts');
			return $this->CI->login_attempts->get_attempts_num($this->CI->input->ip_address(), $login)
					>= $this->CI->config->item('login_max_attempts', 'tank_auth');
		}
		return FALSE;
	}

	/**
	 * Increase number of attempts for given IP-address and login
	 * (if attempts to login is being counted)
	 *
	 * @param	string
	 * @return	void
	 */
	private function increase_login_attempt($login)
	{
		if ($this->CI->config->item('login_count_attempts', 'tank_auth')) {
			if (!$this->is_max_login_attempts_exceeded($login)) {
				$this->CI->load->model('auth/login_attempts');
				$this->CI->login_attempts->increase_attempt($this->CI->input->ip_address(), $login);
			}
		}
	}

	/**
	 * Clear all attempt records for given IP-address and login
	 * (if attempts to login is being counted)
	 *
	 * @param	string
	 * @return	void
	 */
	private function clear_login_attempts($login)
	{
		if ($this->CI->config->item('login_count_attempts', 'tank_auth')) {
			$this->CI->load->model('auth/login_attempts');
			$this->CI->login_attempts->clear_attempts(
					$this->CI->input->ip_address(),
					$login,
					$this->CI->config->item('login_attempt_expire', 'tank_auth'));
		}
	}
}

/* End of file Tank_auth.php */
/* Location: ./application/libraries/Tank_auth.php */