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

/**
 * Aauth is a User Authorization Library for CodeIgniter 2.x, which aims to make 
 * easy some essential jobs such as login, permissions and access operations. 
 * Despite ease of use, it has also very advanced features like private messages, 
 * groupping, access management, public access etc..
 *
 * @author		Emre Akay <emreakayfb@hotmail.com>
 * @contributor Jacob Tomlinson
 * @contributor Tim Swagger (Renowne, LLC) <tim@renowne.com>
 * @contributor Raphael Jackstadt <info@rejack.de>
 *
 * @copyright 2014-2016 Emre Akay
 *
 * @version 2.5.11
 *
 * @license LGPL
 * @license http://opensource.org/licenses/LGPL-3.0 Lesser GNU Public License
 *
 * The latest version of Aauth can be obtained from:
 * https://github.com/emreakay/CodeIgniter-Aauth
 *
 * @todo separate (on some level) the unvalidated users from the "banned" users
 */
class Aauth {

	/**
	 * The CodeIgniter object variable
	 * @access public
	 * @var object
	 */
	public $CI;

	/**
	 * Variable for loading the config array into
	 * @access public
	 * @var array
	 */
	public $config_vars;

	/**
	 * Array to store error messages
	 * @access public
	 * @var array
	 */
	public $errors = array();

	/**
	 * Array to store info messages
	 * @access public
	 * @var array
	 */
	public $infos = array();
	
	/**
	 * Local temporary storage for current flash errors
	 *
	 * Used to update current flash data list since flash data is only available on the next page refresh
	 * @access public
	 * var array
	 */
	public $flash_errors = array();

	/**
	 * Local temporary storage for current flash infos
	 *
	 * Used to update current flash data list since flash data is only available on the next page refresh
	 * @access public
	 * var array
	 */
	public $flash_infos = array();

	/**
     * The CodeIgniter object variable
	 * @access public
     * @var object
     */
    public $aauth_db;

	########################
	# Base Functions
	########################

	/**
	 * Constructor
	 */
	public function __construct() {

		// get main CI object
		$this->CI = & get_instance();

		// Dependancies
		if(CI_VERSION >= 2.2){
			$this->CI->load->library('driver');
		}
		$this->CI->load->library('session');
		$this->CI->lang->load('aauth');

 		// config/aauth.php
		$this->CI->config->load('aauth');
		$this->config_vars = $this->CI->config->item('aauth');

		$this->aauth_db = $this->CI->load->database($this->config_vars['db_profile'], TRUE); 
		
		// load error and info messages from flashdata (but don't store back in flashdata)
		$this->errors = $this->CI->session->flashdata('errors') ?: array();
		$this->infos = $this->CI->session->flashdata('infos') ?: array();
	}


	########################
	# Login Functions
	########################

	//tested
	/**
	 * Login user
	 * Check provided details against the database. Add items to error array on fail, create session if success
	 * @param string $email
	 * @param string $pass
	 * @param bool $remember
	 * @return bool Indicates successful login.
	 */
	public function login($identifier, $pass, $remember = FALSE, $totp_code = NULL, $set_userdata = TRUE) {

		// Remove cookies first
		$cookie = array(
			'name'	 => 'user',
			'value'	 => '',
			'expire' => -3600,
			'path'	 => '/',
		);

		if ($set_userdata) {
			$this->CI->input->set_cookie($cookie);
		}
		if ($this->config_vars['ddos_protection'] && ! $this->update_login_attempts()) {

			$this->error($this->CI->lang->line('aauth_error_login_attempts_exceeded'));
			return FALSE;
		}
		if($this->config_vars['ddos_protection'] && $this->config_vars['recaptcha_active'] && $this->get_login_attempts() > $this->config_vars['recaptcha_login_attempts']){
			$this->CI->load->helper('recaptchalib');
			$reCaptcha = new ReCaptcha( $this->config_vars['recaptcha_secret']);
			$resp = $reCaptcha->verifyResponse( $this->CI->input->server("REMOTE_ADDR"), $this->CI->input->post("g-recaptcha-response") );

			if( ! $resp->success){
				$this->error($this->CI->lang->line('aauth_error_recaptcha_not_correct'));
				return FALSE;
			}
		}
 		if( $this->config_vars['login_with_name'] == TRUE){

			if( !$identifier OR strlen($pass) < $this->config_vars['min'] OR strlen($pass) > $this->config_vars['max'] )
			{
				$this->error($this->CI->lang->line('aauth_error_login_failed_name'));
				return FALSE;
			}
			$db_identifier = 'username';
 		}else{
			$this->CI->load->helper('email');
			if( !valid_email($identifier) OR strlen($pass) < $this->config_vars['min'] OR strlen($pass) > $this->config_vars['max'] )
			{
				$this->error($this->CI->lang->line('aauth_error_login_failed_email'));
				return FALSE;
			}
			$db_identifier = 'email';
 		}

		// if user is not verified
		$query = null;
		$query = $this->aauth_db->where($db_identifier, $identifier);
		$query = $this->aauth_db->where('banned', 1);
		$query = $this->aauth_db->where('verification_code !=', '');
		$query = $this->aauth_db->get($this->config_vars['users']);

		if ($query->num_rows() > 0) {
			$this->error($this->CI->lang->line('aauth_error_account_not_verified'));
			return FALSE;
		}

		// to find user id, create sessions and cookies
		$query = $this->aauth_db->where($db_identifier, $identifier);
		$query = $this->aauth_db->get($this->config_vars['users']);
		
		if($query->num_rows() == 0){
			$this->error($this->CI->lang->line('aauth_error_no_user'));
			return FALSE;
		}
		if($this->config_vars['totp_active'] == TRUE AND $this->config_vars['totp_only_on_ip_change'] == FALSE AND $this->config_vars['totp_two_step_login_active'] == FALSE){
			if($this->config_vars['totp_two_step_login_active'] == TRUE){
				$this->CI->session->set_userdata('totp_required', true);
			}

			$query = null;
			$query = $this->aauth_db->where($db_identifier, $identifier);
			$query = $this->aauth_db->get($this->config_vars['users']);
			$totp_secret =  $query->row()->totp_secret;
			if ($query->num_rows() > 0 AND !$totp_code) {
				$this->error($this->CI->lang->line('aauth_error_totp_code_required'));
				return FALSE;
			}else {
				if(!empty($totp_secret)){
					$this->CI->load->helper('googleauthenticator');
					$ga = new PHPGangsta_GoogleAuthenticator();
					$checkResult = $ga->verifyCode($totp_secret, $totp_code, 0);
					if (!$checkResult) {
						$this->error($this->CI->lang->line('aauth_error_totp_code_invalid'));
						return FALSE;
					}
				}
			}
	 	}
	 	
	 	if($this->config_vars['totp_active'] == TRUE AND $this->config_vars['totp_only_on_ip_change'] == TRUE){
			$query = null;
			$query = $this->aauth_db->where($db_identifier, $identifier);
			$query = $this->aauth_db->get($this->config_vars['users']);
			$totp_secret =  $query->row()->totp_secret;
			$ip_address = $query->row()->ip_address;
			$current_ip_address = $this->CI->input->ip_address();

			if ($query->num_rows() > 0 AND !$totp_code) {
				if($ip_address != $current_ip_address ){
					if($this->config_vars['totp_two_step_login_active'] == FALSE){
						$this->error($this->CI->lang->line('aauth_error_totp_code_required'));
						return FALSE;
					} else if($this->config_vars['totp_two_step_login_active'] == TRUE){
						$this->CI->session->set_userdata('totp_required', true);
					}
				}
			}else {
				if(!empty($totp_secret)){
					if($ip_address != $current_ip_address ){
						$this->CI->load->helper('googleauthenticator');
						$ga = new PHPGangsta_GoogleAuthenticator();
						$checkResult = $ga->verifyCode($totp_secret, $totp_code, 0);
						if (!$checkResult) {
							$this->error($this->CI->lang->line('aauth_error_totp_code_invalid'));
							return FALSE;
						}
					}
				}
			}
	 	}
	 	
		$query = null;
		$query = $this->aauth_db->where($db_identifier, $identifier);
		$query = $this->aauth_db->where('banned', 0);

		$query = $this->aauth_db->get($this->config_vars['users']);

		$row = $query->row();

		// if email and pass matches and not banned
		if ($row) {
			$password = ($this->config_vars['use_password_hash'] ? $pass : $this->hash_password($pass, $row->id));
		} else {
			$password = '';
		}

		if ( $query->num_rows() != 0 && $this->verify_password($password, $row->pass) ) {

			// If email and pass matches
			// create session
			$data = array(
				'id' => $row->id,
				'username' => $row->username,
				'email' => $row->email,
				'loggedin' => TRUE
			);

			if ($set_userdata) {
				$this->CI->session->set_userdata($data);
				if ( $remember ){
					$this->CI->load->helper('string');
					$expire = $this->config_vars['remember'];
					$today = date("Y-m-d");
					$remember_date = date("Y-m-d", strtotime($today . $expire) );
					$random_string = random_string('alnum', 16);
					$this->update_remember($row->id, $random_string, $remember_date );
					$cookie = array(
						'name'	 => 'user',
						'value'	 => $row->id . "-" . $random_string,
						'expire' => 99*999*999,
						'path'	 => '/',
					);
					$this->CI->input->set_cookie($cookie);
				}
			}

			
			// update last login
			$this->update_last_login($row->id);
			$this->update_activity();

			if($this->config_vars['remove_successful_attempts'] == TRUE){
				$this->reset_login_attempts();
			}
			
			return $row;
		}
		// if not matches
		else {

			$this->error($this->CI->lang->line('aauth_error_login_failed_all'));
			return FALSE;
		}
	}

	//tested
	/**
	 * Check user login
	 * Checks if user logged in, also checks remember.
	 * @return bool
	 */
	public function is_loggedin() {

		if ( $this->CI->session->userdata('loggedin') ){ 
			return TRUE; 
		} else {
			if( ! $this->CI->input->cookie('user', TRUE) ){
				return FALSE;
			} else {
				$cookie = explode('-', $this->CI->input->cookie('user', TRUE));
				if(!is_numeric( $cookie[0] ) OR strlen($cookie[1]) < 13 ){return FALSE;}
				else{
					$query = $this->aauth_db->where('id', $cookie[0]);
					$query = $this->aauth_db->where('remember_exp', $cookie[1]);
					$query = $this->aauth_db->get($this->config_vars['users']);

					$row = $query->row();

					if ($query->num_rows() < 1) {
						$this->update_remember($cookie[0]);
						return FALSE;
					}else{

						if(strtotime($row->remember_time) > strtotime("now") ){
							$this->login_fast($cookie[0]);
							return TRUE;
						}
						// if time is expired
						else {
							return FALSE;
						}
					}
				}
			}
		}
		return FALSE;
	}

	/**
	 * Controls if a logged or public user has permission
	 * 
	 * If user does not have permission to access page, it stops script and gives
	 * error message, unless 'no_permission' value is set in config.  If 'no_permission' is
	 * set in config it redirects user to the set url and passes the 'no_access' error message.
	 * It also updates last activity every time function called.
	 *
	 * @param bool $perm_par If not given just control user logged in or not
	 */
	public function control( $perm_par = FALSE ){
		
		$this->CI->load->helper('url');

		if($this->CI->session->userdata('totp_required')){
			$this->error($this->CI->lang->line('aauth_error_totp_verification_required'));
			redirect($this->config_vars['totp_two_step_login_redirect']);			
		}

		$perm_id = $this->get_perm_id($perm_par);
		$this->update_activity();
		if($perm_par == FALSE){
			if($this->is_loggedin()){
				return TRUE;				
			}else if(!$this->is_loggedin()){
				$this->error($this->CI->lang->line('aauth_error_no_access'));
				if($this->config_vars['no_permission'] !== FALSE){
					redirect($this->config_vars['no_permission']);			
				}
			}

		}else if ( ! $this->is_allowed($perm_id) OR ! $this->is_group_allowed($perm_id) ){
			if( $this->config_vars['no_permission'] ) {
				$this->error($this->CI->lang->line('aauth_error_no_access'));
				if($this->config_vars['no_permission'] !== FALSE){
					redirect($this->config_vars['no_permission']);			
				}
			}
			else {
				echo $this->CI->lang->line('aauth_error_no_access');
				die();
			}
		}
	}

	//tested
	/**
	 * Logout user
	 * Destroys the CodeIgniter session and remove cookies to log out user.
	 * @return bool If session destroy successful
	 */
	public function logout() {

		$cookie = array(
			'name'	 => 'user',
			'value'	 => '',
			'expire' => -3600,
			'path'	 => '/',
		);
		$this->CI->input->set_cookie($cookie);
		
		return $this->CI->session->sess_destroy();
	}

	//tested
	/**
	 * Fast login
	 * Login with just a user id
	 * @param int $user_id User id to log in
	 * @return bool TRUE if login successful.
	 */
	public function login_fast($user_id){

		$query = $this->aauth_db->where('id', $user_id);
		$query = $this->aauth_db->where('banned', 0);
		$query = $this->aauth_db->get($this->config_vars['users']);

		$row = $query->row();

		if ($query->num_rows() > 0) {

			// if id matches
			// create session
			$data = array(
				'id' => $row->id,
				'username' => $row->username,
				'email' => $row->email,
				'loggedin' => TRUE
			);

			$this->CI->session->set_userdata($data);
			return TRUE;
		}
		return FALSE;
	}

	/**
	 * Reset last login attempts
	 * Removes a Login Attempt 
	 * @return bool Reset fails/succeeds
	 */
	public function reset_login_attempts() {
		$ip_address = $this->CI->input->ip_address();
		$this->aauth_db->where(
			array(
				'ip_address'=>$ip_address,
				'timestamp >='=>date("Y-m-d H:i:s", strtotime("-".$this->config_vars['max_login_attempt_time_period']))
			)
		);
		return $this->aauth_db->delete($this->config_vars['login_attempts']);
	}

	/**
	 * Remind password
	 * Emails user with link to reset password
	 * @param string $email Email for account to remind
	 * @return bool Remind fails/succeeds
	 */
	public function remind_password($email){

		$query = $this->aauth_db->where( 'email', $email );
		$query = $this->aauth_db->get( $this->config_vars['users'] );

		if ($query->num_rows() > 0){
			$row = $query->row();

			$ver_code = sha1(strtotime("now"));

			$data['verification_code'] = $ver_code;

			$this->aauth_db->where('email', $email);
			$this->aauth_db->update($this->config_vars['users'], $data);

			$this->CI->load->library('email');
			$this->CI->load->helper('url');

			if(isset($this->config_vars['email_config']) && is_array($this->config_vars['email_config'])){
				$this->CI->email->initialize($this->config_vars['email_config']);
			}

			$this->CI->email->from( $this->config_vars['email'], $this->config_vars['name']);
			$this->CI->email->to($row->email);
			$this->CI->email->subject($this->CI->lang->line('aauth_email_reset_subject'));
			$this->CI->email->message($this->CI->lang->line('aauth_email_reset_text') . site_url() . $this->config_vars['reset_password_link'] . $ver_code );
			$this->CI->email->send();
			
			return TRUE;
		}
		return FALSE;
	}

	/**
	 * Reset password
	 * Generate new password and email it to the user
	 * @param string $ver_code Verification code for account
	 * @return bool Password reset fails/succeeds
	 */
	public function reset_password($ver_code){

		$query = $this->aauth_db->where('verification_code', $ver_code);
		$query = $this->aauth_db->get( $this->config_vars['users'] );

		$this->CI->load->helper('string');
		$pass_length = ($this->config_vars['min']&1 ? $this->config_vars['min']+1 : $this->config_vars['min']);
		$pass = random_string('alnum', $pass_length);

		if( $query->num_rows() > 0 ){

			$row = $query->row();
			$data =	 array(
				'verification_code' => '',
				'pass' => $this->hash_password($pass, $row->id)
			);

		 	if($this->config_vars['totp_active'] == TRUE AND $this->config_vars['totp_reset_over_reset_password'] == TRUE){
		 		$data['totp_secret'] = NULL;
		 	}
		 	
			$email = $row->email;

			$this->aauth_db->where('id', $row->id);
			$this->aauth_db->update($this->config_vars['users'] , $data);

			$this->CI->load->library('email');

			if(isset($this->config_vars['email_config']) && is_array($this->config_vars['email_config'])){
				$this->CI->email->initialize($this->config_vars['email_config']);
			}

			$this->CI->email->from( $this->config_vars['email'], $this->config_vars['name']);
			$this->CI->email->to($email);
			$this->CI->email->subject($this->CI->lang->line('aauth_email_reset_success_subject'));
			$this->CI->email->message($this->CI->lang->line('aauth_email_reset_success_new_password') . $pass);
			$this->CI->email->send();

			return TRUE;
		}

		return FALSE;
	}

	//tested
	/**
	 * Update last login
	 * Update user's last login date
	 * @param int|bool $user_id User id to update or FALSE for current user
	 * @return bool Update fails/succeeds
	 */
	public function update_last_login($user_id = FALSE) {

		if ($user_id == FALSE)
			$user_id = $this->CI->session->userdata('id');

		$data['last_login'] = date("Y-m-d H:i:s");
		$data['ip_address'] = $this->CI->input->ip_address();

		$this->aauth_db->where('id', $user_id);
		return $this->aauth_db->update($this->config_vars['users'], $data);
	}


	//tested
	/**
	 * Update login attempt and if exceeds return FALSE
	 * @return bool
	 */
	public function update_login_attempts() {
		$ip_address = $this->CI->input->ip_address();
		$query = $this->aauth_db->where(
			array(
				'ip_address'=>$ip_address,
				'timestamp >='=>date("Y-m-d H:i:s", strtotime("-".$this->config_vars['max_login_attempt_time_period']))
			)
		);
		$query = $this->aauth_db->get( $this->config_vars['login_attempts'] );

		if($query->num_rows() == 0){
			$data = array();
			$data['ip_address'] = $ip_address;
			$data['timestamp']= date("Y-m-d H:i:s");
			$data['login_attempts']= 1;
			$this->aauth_db->insert($this->config_vars['login_attempts'], $data);
			return TRUE;
		}else{
			$row = $query->row();
			$data = array();
			$data['timestamp'] = date("Y-m-d H:i:s");
			$data['login_attempts'] = $row->login_attempts + 1;
			$this->aauth_db->where('id', $row->id);
			$this->aauth_db->update($this->config_vars['login_attempts'], $data);

			if ( $data['login_attempts'] > $this->config_vars['max_login_attempt'] ) {
				return FALSE;
			} else {
				return TRUE;
			}
		}

	}

	/**
	 * Get login attempt
	 * @return int
	 */
	public function get_login_attempts() {
		$ip_address = $this->CI->input->ip_address();
		$query = $this->aauth_db->where(
			array(
				'ip_address'=>$ip_address,
				'timestamp >='=>date("Y-m-d H:i:s", strtotime("-".$this->config_vars['max_login_attempt_time_period']))
			)
		);
		$query = $this->aauth_db->get( $this->config_vars['login_attempts'] );

		if($query->num_rows() != 0){
			$row = $query->row();
			return $row->login_attempts;
		}

		return 0;
	}

	/**
	 * Update remember
	 * Update amount of time a user is remembered for
	 * @param int $user_id User id to update 
	 * @param int $expression
	 * @param int $expire 
	 * @return bool Update fails/succeeds
	 */
	public function update_remember($user_id, $expression=null, $expire=null) {

		$data['remember_time'] = $expire;
		$data['remember_exp'] = $expression;

		$query = $this->aauth_db->where('id',$user_id);
		return $this->aauth_db->update($this->config_vars['users'], $data);
	}


	########################
	# User Functions
	########################

	//tested
	/**
	 * Create user
	 * Creates a new user
	 * @param string $email User's email address
	 * @param string $pass User's password
	 * @param string $username User's username
	 * @return int|bool False if create fails or returns user id if successful
	 */
	public function create_user($email, $pass, $username = FALSE, $optional_data = []) {

		$valid = TRUE;

		if($this->config_vars['login_with_name'] == TRUE){
			if (empty($username)){
				$this->error($this->CI->lang->line('aauth_error_username_required'));
				$valid = FALSE;
			}
		}
		if ($this->user_exist_by_username($username) && $username != FALSE) {
			$this->error($this->CI->lang->line('aauth_error_username_exists'));
			$valid = FALSE;
		}

		if ($this->user_exist_by_email($email)) {
			$this->error($this->CI->lang->line('aauth_error_email_exists'));
			$valid = FALSE;
		}
		$valid_email = (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
		if (!$valid_email){
			$this->error($this->CI->lang->line('aauth_error_email_invalid'));
			$valid = FALSE;
		}
		if ( strlen($pass) < $this->config_vars['min'] OR strlen($pass) > $this->config_vars['max'] ){
			$this->error($this->CI->lang->line('aauth_error_password_invalid'));
			$valid = FALSE;
		}
		if ($username != FALSE && !ctype_alnum(str_replace($this->config_vars['additional_valid_chars'], '', $username))){
			$this->error($this->CI->lang->line('aauth_error_username_invalid'));
			$valid = FALSE;
		}


		if (!$valid) {
			return FALSE; 
		}

		$data = array(
			'email' => $email,
			'pass' => $this->hash_password($pass, 0), // Password cannot be blank but user_id required for salt, setting bad password for now
			'username' => (!$username) ? '' : $username ,
			'date_created' => date("Y-m-d H:i:s"),
		);


		if (count($optional_data) > 0) {
			$data = array_merge($data, $optional_data);
		}

		if ( $this->aauth_db->insert($this->config_vars['users'], $data )){
			$user_id = $this->aauth_db->insert_id();
			// set default group
			$this->add_member($user_id, $this->config_vars['default_group']);

			// if verification activated
			if($this->config_vars['verification'] && !$this->is_admin()){
				$data = null;
				$data['banned'] = 1;

				$this->aauth_db->where('id', $user_id);
				$this->aauth_db->update($this->config_vars['users'], $data);

				// sends verifition ( !! e-mail settings must be set)
				$this->send_verification($user_id);
			}

			// Update to correct salted password
			if( !$this->config_vars['use_password_hash']){
				$data = null;
				$data['pass'] = $this->hash_password($pass, $user_id);
				$this->aauth_db->where('id', $user_id);
				$this->aauth_db->update($this->config_vars['users'], $data);
			}

			return $user_id;

		} else {
			return FALSE;
		}
	}

	//tested
	/**
	 * Update user
	 * Updates existing user details
	 * @param int $user_id User id to update
	 * @param string|bool $email User's email address, or FALSE if not to be updated
	 * @param string|bool $pass User's password, or FALSE if not to be updated
	 * @param string|bool $name User's name, or FALSE if not to be updated
	 * @return bool Update fails/succeeds
	 */
	public function update_user($user_id, $email = FALSE, $pass = FALSE, $username = FALSE, $optional_data = []) {

		$data = array();
		$valid = TRUE;
		$user = $this->get_user($user_id);

		if (count($optional_data) > 0) {
			$data = array_merge($data, $optional_data);
		}

		if ($user->email == $email) {
			$email = FALSE;
		}
		
		if ($email != FALSE) {
			if ($this->user_exist_by_email($email)) {
				$this->error($this->CI->lang->line('aauth_error_update_email_exists'));
				$valid = FALSE;
			}
			$valid_email = (bool) filter_var($email, FILTER_VALIDATE_EMAIL);
			if (!$valid_email){
				$this->error($this->CI->lang->line('aauth_error_email_invalid'));
				$valid = FALSE;
			}
			$data['email'] = $email;
		}

		if ($pass != FALSE) {
			if ( strlen($pass) < $this->config_vars['min'] OR strlen($pass) > $this->config_vars['max'] ){
				$this->error($this->CI->lang->line('aauth_error_password_invalid'));
				$valid = FALSE;
			}
			$data['pass'] = $this->hash_password($pass, $user_id);
		}

		if ($user->username == $username) {
			$username = FALSE;
		}

		if ($username != FALSE) {
			if ($this->user_exist_by_username($username)) {
				$this->error($this->CI->lang->line('aauth_error_update_username_exists'));
				$valid = FALSE;
			}
			if ($username !='' && !ctype_alnum(str_replace($this->config_vars['additional_valid_chars'], '', $username))){
				$this->error($this->CI->lang->line('aauth_error_username_invalid'));
				$valid = FALSE;
			}
			$data['username'] = $username;
		}

		if ( !$valid || empty($data)) {
			return FALSE; 
		}
		
		$this->aauth_db->where('id', $user_id);
		return $this->aauth_db->update($this->config_vars['users'], $data);
	}

	//tested
	/**
	 * List users
	 * Return users as an object array
	 * @param bool|int $group_par Specify group id to list group or FALSE for all users
	 * @param string $limit Limit of users to be returned
	 * @param bool $offset Offset for limited number of users
	 * @param bool $include_banneds Include banned users
	 * @return array Array of users
	 */
	public function list_users($group_par = FALSE, $limit = FALSE, $offset = FALSE, $include_banneds = FALSE) {

		// if group_par is given
		if ($group_par != FALSE) {

			$group_par = $this->get_group_id($group_par);
			$this->aauth_db->select('*')
				->from($this->config_vars['users'])
				->join($this->config_vars['user_to_group'], $this->config_vars['users'] . ".id = " . $this->config_vars['user_to_group'] . ".user_id")
				->where($this->config_vars['user_to_group'] . ".group_id", $group_par);

			// if group_par is not given, lists all users
		} else {

			$this->aauth_db->select('*')
				->from($this->config_vars['users']);
		}

		// banneds
		if (!$include_banneds) {
			$this->aauth_db->where('banned != ', 1);
		}

		// limit
		if ($limit) {

			if ($offset == FALSE)
				$this->aauth_db->limit($limit);
			else
				$this->aauth_db->limit($limit, $offset);
		}

		$query = $this->aauth_db->get();

		return $query->result();
	}

	//tested
	/**
	 * Get user
	 * Get user information
	 * @param int|bool $user_id User id to get or FALSE for current user
	 * @return object User information
	 */
	public function get_user($user_id = FALSE) {

		if ($user_id == FALSE)
			$user_id = $this->CI->session->userdata('id');

		$query = $this->aauth_db->where('id', $user_id);
		$query = $this->aauth_db->get($this->config_vars['users']);

		if ($query->num_rows() <= 0){
			$this->error($this->CI->lang->line('aauth_error_no_user'));
			return FALSE;
		}
		return $query->row();
	}

	/**
	 * Verify user
	 * Activates user account based on verification code
	 * @param int $user_id User id to activate
	 * @param string $ver_code Code to validate against
	 * @return bool Activation fails/succeeds
	 */
	public function verify_user($user_id, $ver_code){

		$query = $this->aauth_db->where('id', $user_id);
		$query = $this->aauth_db->where('verification_code', $ver_code);
		$query = $this->aauth_db->get( $this->config_vars['users'] );

		// if ver code is TRUE
		if( $query->num_rows() > 0 ){

			$data =	 array(
				'verification_code' => '',
				'banned' => 0
			);

			$this->aauth_db->where('id', $user_id);
			$this->aauth_db->update($this->config_vars['users'] , $data);
			return TRUE;
		}
		return FALSE;
	}

	/**
	 * Send verification email
	 * Sends a verification email based on user id
	 * @param int $user_id User id to send verification email to
	 * @todo return success indicator
	 */
	public function send_verification($user_id){

		$query = $this->aauth_db->where( 'id', $user_id );
		$query = $this->aauth_db->get( $this->config_vars['users'] );

		if ($query->num_rows() > 0){
			$row = $query->row();

			$this->CI->load->helper('string');
			$ver_code = random_string('alnum', 16);

			$data['verification_code'] = $ver_code;

			$this->aauth_db->where('id', $user_id);
			$this->aauth_db->update($this->config_vars['users'], $data);

			$this->CI->load->library('email');
			$this->CI->load->helper('url');

			if(isset($this->config_vars['email_config']) && is_array($this->config_vars['email_config'])){
				$this->CI->email->initialize($this->config_vars['email_config']);
			}
	
			$this->CI->email->from( $this->config_vars['email'], $this->config_vars['name']);
			$this->CI->email->to($row->email);
			$this->CI->email->subject($this->CI->lang->line('aauth_email_verification_subject'));
			$this->CI->email->message($this->CI->lang->line('aauth_email_verification_code') . $ver_code .
				$this->CI->lang->line('aauth_email_verification_text') . site_url() .$this->config_vars['verification_link'] . $user_id . '/' . $ver_code );
			$this->CI->email->send();
		}
	}

	//not tested excatly
	/**
	 * Delete user
	 * Delete a user from database. WARNING Can't be undone
	 * @param int $user_id User id to delete
	 * @return bool Delete fails/succeeds
	 */
	public function delete_user($user_id) {

		// delete from perm_to_user
		$this->aauth_db->where('user_id', $user_id);
		$this->aauth_db->delete($this->config_vars['perm_to_user']);

		// delete from user_to_group
		$this->aauth_db->where('user_id', $user_id);
		$this->aauth_db->delete($this->config_vars['user_to_group']);

		// delete user vars
		$this->aauth_db->where('user_id', $user_id);
		$this->aauth_db->delete($this->config_vars['user_variables']);

		// delete user
		$this->aauth_db->where('id', $user_id);
		return $this->aauth_db->delete($this->config_vars['users']);

	}

	//tested
	/**
	 * Ban user
	 * Bans a user account
	 * @param int $user_id User id to ban
	 * @return bool Ban fails/succeeds
	 */
	public function ban_user($user_id) {

		$data = array(
			'banned' => 1,
			'verification_code' => ''
		);

		$this->aauth_db->where('id', $user_id);

		return $this->aauth_db->update($this->config_vars['users'], $data);
	}

	//tested
	/**
	 * Unban user
	 * Activates user account
	 * Same with unlock_user()
	 * @param int $user_id User id to activate
	 * @return bool Activation fails/succeeds
	 */
	public function unban_user($user_id) {

		$data = array(
			'banned' => 0
		);

		$this->aauth_db->where('id', $user_id);

		return $this->aauth_db->update($this->config_vars['users'], $data);
	}

	//tested
	/**
	 * Check user banned
	 * Checks if a user is banned
	 * @param int $user_id User id to check
	 * @return bool False if banned, True if not
	 */
	public function is_banned($user_id) {

		$query = $this->aauth_db->where('id', $user_id);
		$query = $this->aauth_db->where('banned', 1);

		$query = $this->aauth_db->get($this->config_vars['users']);

		if ($query->num_rows() > 0)
			return TRUE;
		else
			return FALSE;
	}

	/**
	 * user_exist_by_username
	 * Check if user exist by username
	 * @param $user_id
	 *
	 * @return bool
	 */
	public function user_exist_by_username( $name ) {
		$query = $this->aauth_db->where('username', $name);

		$query = $this->aauth_db->get($this->config_vars['users']);

		if ($query->num_rows() > 0)
			return TRUE;
		else
			return FALSE;
	}

	/**
	 * user_exist_by_name !DEPRECATED!
	 * Check if user exist by name
	 * @param $user_id
	 *
	 * @return bool
	 */
	public function user_exist_by_name( $name ) {
		return $this->user_exist_by_username($name);
	}

	/**
	 * user_exist_by_email
	 * Check if user exist by user email
	 * @param $user_email
	 *
	 * @return bool
	 */
	public function user_exist_by_email( $user_email ) {
		$query = $this->aauth_db->where('email', $user_email);

		$query = $this->aauth_db->get($this->config_vars['users']);

		if ($query->num_rows() > 0)
			return TRUE;
		else
			return FALSE;
	}

	/**
	 * user_exist_by_id
	 * Check if user exist by user email
	 * @param $user_email
	 *
	 * @return bool
	 */
	public function user_exist_by_id( $user_id ) {
		$query = $this->aauth_db->where('id', $user_id);

		$query = $this->aauth_db->get($this->config_vars['users']);

		if ($query->num_rows() > 0)
			return TRUE;
		else
			return FALSE;
	}

	/**
	 * Get user id
	 * Get user id from email address, if par. not given, return current user's id
	 * @param string|bool $email Email address for user
	 * @return int User id
	 */
	public function get_user_id($email=FALSE) {

		if( ! $email){
			$query = $this->aauth_db->where('id', $this->CI->session->userdata('id'));
		} else {
			$query = $this->aauth_db->where('email', $email);
		}

		$query = $this->aauth_db->get($this->config_vars['users']);

		if ($query->num_rows() <= 0){
			$this->error($this->CI->lang->line('aauth_error_no_user'));
			return FALSE;
		}
		return $query->row()->id;
	}

	/**
	 * Get user groups
	 * Get groups a user is in
	 * @param int|bool $user_id User id to get or FALSE for current user
	 * @return array Groups
	 */
	public function get_user_groups($user_id = FALSE){

		if( !$user_id) { $user_id = $this->CI->session->userdata('id'); }
		if( !$user_id){
			$this->aauth_db->where('name', $this->config_vars['public_group']);
			$query = $this->aauth_db->get($this->config_vars['groups']);
		}else if($user_id){
			$this->aauth_db->join($this->config_vars['groups'], "id = group_id");
			$this->aauth_db->where('user_id', $user_id);
			$query = $this->aauth_db->get($this->config_vars['user_to_group']);
		}
		return $query->result();
	}

	//tested
	/**
	 * Update activity
	 * Update user's last activity date
	 * @param int|bool $user_id User id to update or FALSE for current user
	 * @return bool Update fails/succeeds
	 */
	public function update_activity($user_id = FALSE) {

		if ($user_id == FALSE)
			$user_id = $this->CI->session->userdata('id');

		if($user_id==FALSE){return FALSE;}

		$data['last_activity'] = date("Y-m-d H:i:s");

		$query = $this->aauth_db->where('id',$user_id);
		return $this->aauth_db->update($this->config_vars['users'], $data);
	}

	//tested
	/**
	 * Hash password
	 * Hash the password for storage in the database
	 * (thanks to Jacob Tomlinson for contribution)
	 * @param string $pass Password to hash
	 * @param $userid
	 * @return string Hashed password
	 */
	function hash_password($pass, $userid) {
		if($this->config_vars['use_password_hash']){
			return password_hash($pass, $this->config_vars['password_hash_algo'], $this->config_vars['password_hash_options']);
		}else{
			$salt = md5($userid);
			return hash($this->config_vars['hash'], $salt.$pass);
		}
	}

	/**
	 * Verify password
	 * Verfies the hashed password
	 * @param string $password Password
	 * @param string $hash Hashed Password
	 * @param string $user_id 
	 * @return bool False or True
	 */
	function verify_password($password, $hash) {
		if($this->config_vars['use_password_hash']){
			return password_verify($password, $hash);
		}else{
			return ($password == $hash ? TRUE : FALSE);
		}
	}

	########################
	# Group Functions
	########################

	//tested
	/**
	 * Create group
	 * Creates a new group
	 * @param string $group_name New group name
	 * @param string $definition Description of the group
	 * @return int|bool Group id or FALSE on fail
	 */
	public function create_group($group_name, $definition = '') {

		$query = $this->aauth_db->get_where($this->config_vars['groups'], array('name' => $group_name));

		if ($query->num_rows() < 1) {

			$data = array(
				'name' => $group_name,
				'definition'=> $definition
			);
			$this->aauth_db->insert($this->config_vars['groups'], $data);
			return $this->aauth_db->insert_id();
		}

		$this->info($this->CI->lang->line('aauth_info_group_exists'));
		return FALSE;
	}

	//tested
	/**
	 * Update group
	 * Change a groups name
	 * @param int $group_id Group id to update
	 * @param string $group_name New group name
	 * @return bool Update success/failure
	 */
	public function update_group($group_par, $group_name=FALSE, $definition=FALSE) {

		$group_id = $this->get_group_id($group_par);

		if ($group_name != FALSE) {
			$data['name'] = $group_name;
		}

		if ($definition != FALSE) {
			$data['definition'] = $definition;
		}


		$this->aauth_db->where('id', $group_id);
		return $this->aauth_db->update($this->config_vars['groups'], $data);
	}

	//tested
	/**
	 * Delete group
	 * Delete a group from database. WARNING Can't be undone
	 * @param int $group_id User id to delete
	 * @return bool Delete success/failure
	 */
	public function delete_group($group_par) {

		$group_id = $this->get_group_id($group_par);
		
		$this->aauth_db->where('id',$group_id);
		$query = $this->aauth_db->get($this->config_vars['groups']);
		if ($query->num_rows() == 0){
			return FALSE;
		}

		// bug fixed
		// now users are deleted from user_to_group table
		$this->aauth_db->where('group_id', $group_id);
		$this->aauth_db->delete($this->config_vars['user_to_group']);
		
		$this->aauth_db->where('group_id', $group_id);
		$this->aauth_db->delete($this->config_vars['perm_to_group']);
		
		$this->aauth_db->where('group_id', $group_id);
		$this->aauth_db->delete($this->config_vars['group_to_group']);

		$this->aauth_db->where('subgroup_id', $group_id);
		$this->aauth_db->delete($this->config_vars['group_to_group']);

		$this->aauth_db->where('id', $group_id);
		return $this->aauth_db->delete($this->config_vars['groups']);
	}

	//tested
	/**
	 * Add member
	 * Add a user to a group
	 * @param int $user_id User id to add to group
	 * @param int|string $group_par Group id or name to add user to
	 * @return bool Add success/failure
	 */
	public function add_member($user_id, $group_par) {

		$group_id = $this->get_group_id($group_par);

		if( ! $group_id ) {

			$this->error( $this->CI->lang->line('aauth_error_no_group') );
			return FALSE;
		}

		$query = $this->aauth_db->where('user_id',$user_id);
		$query = $this->aauth_db->where('group_id',$group_id);
		$query = $this->aauth_db->get($this->config_vars['user_to_group']);

		if ($query->num_rows() < 1) {
			$data = array(
				'user_id' => $user_id,
				'group_id' => $group_id
			);

			return $this->aauth_db->insert($this->config_vars['user_to_group'], $data);
		}
		$this->info($this->CI->lang->line('aauth_info_already_member'));
		return TRUE;
	}

	//tested
	/**
	 * Remove member
	 * Remove a user from a group
	 * @param int $user_id User id to remove from group
	 * @param int|string $group_par Group id or name to remove user from
	 * @return bool Remove success/failure
	 */
	public function remove_member($user_id, $group_par) {

		$group_par = $this->get_group_id($group_par);
		$this->aauth_db->where('user_id', $user_id);
		$this->aauth_db->where('group_id', $group_par);
		return $this->aauth_db->delete($this->config_vars['user_to_group']);
	}
	
	/**
	 * Add subgroup
	 * Add a subgroup to a group
	 * @param int $user_id User id to add to group
	 * @param int|string $group_par Group id or name to add user to
	 * @return bool Add success/failure
	 */
	public function add_subgroup($group_par, $subgroup_par) {

		$group_id = $this->get_group_id($group_par);
		$subgroup_id = $this->get_group_id($subgroup_par);

		if( ! $group_id ) {
			$this->error( $this->CI->lang->line('aauth_error_no_group') );
			return FALSE;
		}

		if( ! $subgroup_id ) {
			$this->error( $this->CI->lang->line('aauth_error_no_subgroup') );
			return FALSE;
		}

		$query = $this->aauth_db->where('group_id',$group_id);
		$query = $this->aauth_db->where('subgroup_id',$subgroup_id);
		$query = $this->aauth_db->get($this->config_vars['group_to_group']);

		if ($query->num_rows() < 1) {
			$data = array(
				'group_id' => $group_id,
				'subgroup_id' => $subgroup_id,
			);

			return $this->aauth_db->insert($this->config_vars['group_to_group'], $data);
		}
		$this->info($this->CI->lang->line('aauth_info_already_subgroup'));
		return TRUE;
	}

	/**
	 * Remove subgroup
	 * Remove a subgroup from a group
	 * @param int|string $group_par Group id or name to remove 
	 * @param int|string $subgroup_par Sub-Group id or name to remove
	 * @return bool Remove success/failure
	 */
	public function remove_subgroup($group_par, $subgroup_par) {

		$group_par = $this->get_group_id($group_par);
		$subgroup_par = $this->get_group_id($subgroup_par);
		$this->aauth_db->where('group_id', $group_par);
		$this->aauth_db->where('subgroup_id', $subgroup_par);
		return $this->aauth_db->delete($this->config_vars['group_to_group']);
	}
	
	//tested
	/**
	 * Remove member
	 * Remove a user from all groups
	 * @param int $user_id User id to remove from all groups
	 * @return bool Remove success/failure
	 */
	public function remove_member_from_all($user_id) {

		$this->aauth_db->where('user_id', $user_id);
		return $this->aauth_db->delete($this->config_vars['user_to_group']);
	}
	//tested
	/**
	 * Is member
	 * Check if current user is a member of a group
	 * @param int|string $group_par Group id or name to check
	 * @param int|bool $user_id User id, if not given current user
	 * @return bool
	 */
	public function is_member( $group_par, $user_id = FALSE ) {

		// if user_id FALSE (not given), current user
		if( ! $user_id){
			$user_id = $this->CI->session->userdata('id');
		}

		$group_id = $this->get_group_id($group_par);

		$query = $this->aauth_db->where('user_id', $user_id);
		$query = $this->aauth_db->where('group_id', $group_id);
		$query = $this->aauth_db->get($this->config_vars['user_to_group']);

		$row = $query->row();

		if ($query->num_rows() > 0) {
			return TRUE;
		} else {
			return FALSE;
		}
	}

	//tested
	/**
	 * Is admin
	 * Check if current user is a member of the admin group
	 * @param int $user_id User id to check, if it is not given checks current user
	 * @return bool
	 */
	public function is_admin( $user_id = FALSE ) {

		return $this->is_member($this->config_vars['admin_group'], $user_id);
	}

	//tested
	/**
	 * List groups
	 * List all groups
	 * @return object Array of groups
	 */
	public function list_groups() {

		$query = $this->aauth_db->get($this->config_vars['groups']);
		return $query->result();
	}


	//tested
	/**
	 * Get group name
	 * Get group name from group id
	 * @param int $group_id Group id to get
	 * @return string Group name
	 */
	public function get_group_name($group_id) {

		$query = $this->aauth_db->where('id', $group_id);
		$query = $this->aauth_db->get($this->config_vars['groups']);

		if ($query->num_rows() == 0)
			return FALSE;

		$row = $query->row();
		return $row->name;
	}

	//tested
	/**
	 * Get group id
	 * Get group id from group name or id ( ! Case sensitive)
	 * @param int|string $group_par Group id or name to get
	 * @return int Group id
	 */
	public function get_group_id ( $group_par ) {

		if( is_numeric($group_par) ) { return $group_par; }

		$query = $this->aauth_db->where('name', $group_par);
		$query = $this->aauth_db->get($this->config_vars['groups']);

		if ($query->num_rows() == 0)
			return FALSE;

		$row = $query->row();
		return $row->id;
	}

	/**
	 * Get subgroups
	 * Get subgroups from group name or id ( ! Case sensitive)
	 * @param int|string $group_par Group id or name to get
	 * @return object Array of subgroup_id's
	 */
	public function get_subgroups ( $group_par ) {

		$group_id = $this->get_group_id($group_par);

		$query = $this->aauth_db->where('group_id', $group_id);
		$query = $this->aauth_db->select('subgroup_id');
		$query = $this->aauth_db->get($this->config_vars['group_to_group']);

		if ($query->num_rows() == 0)
			return FALSE;

		return $query->result();
	}

	########################
	# Permission Functions
	########################

	//tested
	/**
	 * Create permission
	 * Creates a new permission type
	 * @param string $perm_name New permission name
	 * @param string $definition Permission description
	 * @return int|bool Permission id or FALSE on fail
	 */
	public function create_perm($perm_name, $definition='') {

		$query = $this->aauth_db->get_where($this->config_vars['perms'], array('name' => $perm_name));

		if ($query->num_rows() < 1) {

			$data = array(
				'name' => $perm_name,
				'definition'=> $definition
			);
			$this->aauth_db->insert($this->config_vars['perms'], $data);
			return $this->aauth_db->insert_id();
		}
		$this->info($this->CI->lang->line('aauth_info_perm_exists'));
		return FALSE;
	}

	//tested
	/**
	 * Update permission
	 * Updates permission name and description
	 * @param int|string $perm_par Permission id or permission name
	 * @param string $perm_name New permission name
	 * @param string $definition Permission description
	 * @return bool Update success/failure
	 */
	public function update_perm($perm_par, $perm_name=FALSE, $definition=FALSE) {

		$perm_id = $this->get_perm_id($perm_par);

		if ($perm_name != FALSE)
			$data['name'] = $perm_name;

		if ($definition != FALSE)
			$data['definition'] = $definition;

		$this->aauth_db->where('id', $perm_id);
		return $this->aauth_db->update($this->config_vars['perms'], $data);
	}

	//not ok
	/**
	 * Delete permission
	 * Delete a permission from database. WARNING Can't be undone
	 * @param int|string $perm_par Permission id or perm name to delete
	 * @return bool Delete success/failure
	 */
	public function delete_perm($perm_par) {

		$perm_id = $this->get_perm_id($perm_par);

		// deletes from perm_to_gropup table
		$this->aauth_db->where('perm_id', $perm_id);
		$this->aauth_db->delete($this->config_vars['perm_to_group']);

		// deletes from perm_to_user table
		$this->aauth_db->where('perm_id', $perm_id);
		$this->aauth_db->delete($this->config_vars['perm_to_user']);

		// deletes from permission table
		$this->aauth_db->where('id', $perm_id);
		return $this->aauth_db->delete($this->config_vars['perms']);
	}

	/**
	 * Is user allowed
	 * Check if user allowed to do specified action, admin always allowed
	 * first checks user permissions then check group permissions
	 * @param int $perm_par Permission id or name to check
	 * @param int|bool $user_id User id to check, or if FALSE checks current user
	 * @return bool
	 */
	public function is_allowed($perm_par, $user_id=FALSE){

		$this->CI->load->helper('url');

		if ($this->CI->cc_app->eventListen('auth_allowed_permission', [$perm_par]) === false) {
		var_dump($this->CI->cc_app->eventListen('auth_allowed_permission', [$perm_par])); exit();
			return false;
		}

		if($this->CI->session->userdata('totp_required')){
			$this->error($this->CI->lang->line('aauth_error_totp_verification_required'));
			redirect($this->config_vars['totp_two_step_login_redirect']);			
		}

		if( $user_id == FALSE){
			$user_id = $this->CI->session->userdata('id');
		}

		if($this->is_admin($user_id))
		{
			return true;
		}

		$perm_id = $this->get_perm_id($perm_par);
		
		$query = $this->aauth_db->where('perm_id', $perm_id);
		$query = $this->aauth_db->where('user_id', $user_id);
		$query = $this->aauth_db->get( $this->config_vars['perm_to_user'] );

		if( $query->num_rows() > 0){
		    return TRUE;
		} else {
			if( $user_id===FALSE){
				return $this->is_group_allowed($perm_id);
			} else {
				$g_allowed=FALSE;
				foreach( $this->get_user_groups($user_id) as $group ){
					if ( $this->is_group_allowed($perm_id, $group->id) ){
						$g_allowed=TRUE;
					}
				}
				return $g_allowed;
			}
	    }
	}

	/**
	 * Is Group allowed
	 * Check if group is allowed to do specified action, admin always allowed
	 * @param int $perm_par Permission id or name to check
	 * @param int|string|bool $group_par Group id or name to check, or if FALSE checks all user groups
	 * @return bool
	 */
	public function is_group_allowed($perm_par, $group_par=FALSE){

		$perm_id = $this->get_perm_id($perm_par);

		// if group par is given
		if($group_par != FALSE){

			$subgroup_ids = $this->get_subgroups($group_par);
			$group_par = $this->get_group_id($group_par);
			$query = $this->aauth_db->where('perm_id', $perm_id);
			$query = $this->aauth_db->where('group_id', $group_par);
			$query = $this->aauth_db->get( $this->config_vars['perm_to_group'] );
			
			$g_allowed=FALSE;
			if(is_array($subgroup_ids)){
				foreach ($subgroup_ids as $g ){
					if($this->is_group_allowed($perm_id, $g->subgroup_id)){
						$g_allowed=TRUE;
					}
				}
			}

			if( $query->num_rows() > 0){
				$g_allowed=TRUE;
			}
			return $g_allowed;
		}
		// if group par is not given
		// checks current user's all groups
		else {
			// if public is allowed or he is admin
			if ( $this->is_admin( $this->CI->session->userdata('id')) OR
				$this->is_group_allowed($perm_id, $this->config_vars['public_group']) )
			{return TRUE;}

			// if is not login
			if (!$this->is_loggedin()){return FALSE;}

			$group_pars = $this->get_user_groups();
			foreach ($group_pars as $g ){
				if($this->is_group_allowed($perm_id, $g->id)){
					return TRUE;
				}
			}
			return FALSE;
		}
	}

	//tested
	/**
	 * Allow User
	 * Add User to permission
	 * @param int $user_id User id to deny
	 * @param int $perm_par Permission id or name to allow
	 * @return bool Allow success/failure
	 */
	public function allow_user($user_id, $perm_par) {

		$perm_id = $this->get_perm_id($perm_par);

		if( ! $perm_id) {
			return FALSE;
		}

		$query = $this->aauth_db->where('user_id',$user_id);
		$query = $this->aauth_db->where('perm_id',$perm_id);
		$query = $this->aauth_db->get($this->config_vars['perm_to_user']);

		// if not inserted before
		if ($query->num_rows() < 1) {

			$data = array(
				'user_id' => $user_id,
				'perm_id' => $perm_id
			);

			return $this->aauth_db->insert($this->config_vars['perm_to_user'], $data);
		}
		return TRUE;
	}

	//tested
	/**
	 * Deny User
	 * Remove user from permission
	 * @param int $user_id User id to deny
	 * @param int $perm_par Permission id or name to deny
	 * @return bool Deny success/failure
	 */
	public function deny_user($user_id, $perm_par) {

		$perm_id = $this->get_perm_id($perm_par);

		$this->aauth_db->where('user_id', $user_id);
		$this->aauth_db->where('perm_id', $perm_id);

		return $this->aauth_db->delete($this->config_vars['perm_to_user']);
	}

	//tested
	/**
	 * Allow Group
	 * Add group to permission
	 * @param int|string|bool $group_par Group id or name to allow
	 * @param int $perm_par Permission id or name to allow
	 * @return bool Allow success/failure
	 */
	public function allow_group($group_par, $perm_par) {

		$perm_id = $this->get_perm_id($perm_par);

		if( ! $perm_id) {
			return FALSE;
		}

		$group_id = $this->get_group_id($group_par);

		if( ! $group_id) {
			return FALSE;
		}

		$query = $this->aauth_db->where('group_id',$group_id);
		$query = $this->aauth_db->where('perm_id',$perm_id);
		$query = $this->aauth_db->get($this->config_vars['perm_to_group']);

		if ($query->num_rows() < 1) {

			$data = array(
				'group_id' => $group_id,
				'perm_id' => $perm_id
			);

			return $this->aauth_db->insert($this->config_vars['perm_to_group'], $data);
		}

		return TRUE;
	}

	//tested
	/**
	 * Deny Group
	 * Remove group from permission
	 * @param int|string|bool $group_par Group id or name to deny
	 * @param int $perm_par Permission id or name to deny
	 * @return bool Deny success/failure
	 */
	public function deny_group($group_par, $perm_par) {

		$perm_id = $this->get_perm_id($perm_par);
		$group_id = $this->get_group_id($group_par);

		$this->aauth_db->where('group_id', $group_id);
		$this->aauth_db->where('perm_id', $perm_id);

		return $this->aauth_db->delete($this->config_vars['perm_to_group']);
	}

	//tested
	/**
	 * List Permissions
	 * List all permissions
	 * @return object Array of permissions
	 */
	public function list_perms() {

		$query = $this->aauth_db->get($this->config_vars['perms']);
		return $query->result();
	}

	//tested
	/**
	 * Get permission id
	 * Get permission id from permisison name or id
	 * @param int|string $perm_par Permission id or name to get
	 * @return int Permission id or NULL if perm does not exist
	 */
	public function get_perm_id($perm_par) {

		if( is_numeric($perm_par) ) { return $perm_par; }

		$query = $this->aauth_db->where('name', $perm_par);
		$query = $this->aauth_db->get($this->config_vars['perms']);

		if ($query->num_rows() == 0)
			return FALSE;

		$row = $query->row();
		return $row->id;
	}

	########################
	# Private Message Functions
	########################

	//tested
	/**
	 * Send Private Message
	 * Send a private message to another user
	 * @param int $sender_id User id of private message sender
	 * @param int $receiver_id User id of private message receiver
	 * @param string $title Message title/subject
	 * @param string $message Message body/content
	 * @return bool Send successful/failed
	 */
	public function send_pm( $sender_id, $receiver_id, $title, $message ){

		if ( !is_numeric($receiver_id) OR $sender_id == $receiver_id ){
			$this->error($this->CI->lang->line('aauth_error_self_pm'));
			return FALSE;
		}
		if (($this->is_banned($receiver_id) || !$this->user_exist_by_id($receiver_id)) || ($sender_id && ($this->is_banned($sender_id) || !$this->user_exist_by_id($sender_id)))){
			$this->error($this->CI->lang->line('aauth_error_no_user'));
			return FALSE;
		}
		if ( !$sender_id){
			$sender_id = 0;
		}

		if ($this->config_vars['pm_encryption']){
			$this->CI->load->library('encrypt');
			$title = $this->CI->encrypt->encode($title);
			$message = $this->CI->encrypt->encode($message);
		}

		$data = array(
			'sender_id' => $sender_id,
			'receiver_id' => $receiver_id,
			'title' => $title,
			'message' => $message,
			'date_sent' => date('Y-m-d H:i:s')
		);

		return $this->aauth_db->insert( $this->config_vars['pms'], $data );
	}

	/**
	 * Send multiple Private Messages
	 * Send multiple private messages to another users
	 * @param int $sender_id User id of private message sender
	 * @param array $receiver_ids Array of User ids of private message receiver 
	 * @param string $title Message title/subject
	 * @param string $message Message body/content
	 * @return array/bool Array with User ID's as key and TRUE or a specific error message OR FALSE if sender doesn't exist
	 */
	public function send_pms( $sender_id, $receiver_ids, $title, $message ){
		if ($this->config_vars['pm_encryption']){
			$this->CI->load->library('encrypt');
			$title = $this->CI->encrypt->encode($title);
			$message = $this->CI->encrypt->encode($message);
		}
		if ($sender_id && ($this->is_banned($sender_id) || !$this->user_exist_by_id($sender_id))){
			$this->error($this->CI->lang->line('aauth_error_no_user'));
			return FALSE;
		}
		if ( !$sender_id){
			$sender_id = 0;
		}
		if (is_numeric($receiver_ids)) {
			$receiver_ids = array($receiver_ids);
		}

		$return_array = array();
		foreach ($receiver_ids as $receiver_id) {
			if ($sender_id == $receiver_id ){
				$return_array[$receiver_id] = $this->CI->lang->line('aauth_error_self_pm');
				continue;
			}
			if ($this->is_banned($receiver_id) || !$this->user_exist_by_id($receiver_id)){
				$return_array[$receiver_id] = $this->CI->lang->line('aauth_error_no_user');
				continue;
			}
			
			$data = array(
				'sender_id' => $sender_id,
				'receiver_id' => $receiver_id,
				'title' => $title,
				'message' => $message,
				'date_sent' => date('Y-m-d H:i:s')
			);
			$return_array[$receiver_id] = $this->aauth_db->insert( $this->config_vars['pms'], $data );
		}

		return $return_array;
	}

	//tested
	/**
	 * List Private Messages
	 * If receiver id not given retruns current user's pms, if sender_id given, it returns only pms from given sender
	 * @param int $limit Number of private messages to be returned
	 * @param int $offset Offset for private messages to be returned (for pagination)
	 * @param int $sender_id User id of private message sender
	 * @param int $receiver_id User id of private message receiver
	 * @return object Array of private messages
	 */
	public function list_pms($limit=5, $offset=0, $receiver_id=NULL, $sender_id=NULL){
		if (is_numeric($receiver_id)){
			$query = $this->aauth_db->where('receiver_id', $receiver_id);
			$query = $this->aauth_db->where('pm_deleted_receiver', NULL);
		}
		if (is_numeric($sender_id)){
			$query = $this->aauth_db->where('sender_id', $sender_id);
			$query = $this->aauth_db->where('pm_deleted_sender', NULL);
		}

		$query = $this->aauth_db->order_by('id','DESC');
		$query = $this->aauth_db->get( $this->config_vars['pms'], $limit, $offset);

		$result = $query->result();

		if ($this->config_vars['pm_encryption']){
			$this->CI->load->library('encrypt');

			foreach ($result as $k => $r)
			{
				$result[$k]->title = $this->CI->encrypt->decode($r->title);
				$result[$k]->message = $this->CI->encrypt->decode($r->message);
			}
		}

		return $result;
	}

	//tested
	/**
	 * Get Private Message
	 * Get private message by id
	 * @param int $pm_id Private message id to be returned
	 * @param int $user_id User ID of Sender or Receiver
	 * @param bool $set_as_read Whether or not to mark message as read
	 * @return object Private message
	 */
	public function get_pm($pm_id, $user_id = NULL, $set_as_read = TRUE){
		if(!$user_id){
			$user_id = $this->CI->session->userdata('id');
		}
		if( !is_numeric($user_id) || !is_numeric($pm_id)){
			$this->error( $this->CI->lang->line('aauth_error_no_pm') );
			return FALSE;
		}

		$query = $this->aauth_db->where('id', $pm_id);
		$query = $this->aauth_db->group_start();
		$query = $this->aauth_db->where('receiver_id', $user_id);
		$query = $this->aauth_db->or_where('sender_id', $user_id);
		$query = $this->aauth_db->group_end();
		$query = $this->aauth_db->get( $this->config_vars['pms'] );

		if ($query->num_rows() < 1) {
			$this->error( $this->CI->lang->line('aauth_error_no_pm') );
			return FALSE;
		}

		$result = $query->row();

		if ($user_id == $result->receiver_id && $set_as_read){
			$this->set_as_read_pm($pm_id);
		}

		if ($this->config_vars['pm_encryption']){
			$this->CI->load->library('encrypt');
			$result->title = $this->CI->encrypt->decode($result->title);
			$result->message = $this->CI->encrypt->decode($result->message);
		}

		return $result;
	}

	//tested
	/**
	 * Delete Private Message
	 * Delete private message by id
	 * @param int $pm_id Private message id to be deleted
	 * @return bool Delete success/failure
	 */
	public function delete_pm($pm_id, $user_id = NULL){
		if(!$user_id){
			$user_id = $this->CI->session->userdata('id');
		}
		if( !is_numeric($user_id) || !is_numeric($pm_id)){
			$this->error( $this->CI->lang->line('aauth_error_no_pm') );
			return FALSE;
		}

		$query = $this->aauth_db->where('id', $pm_id);
		$query = $this->aauth_db->group_start();
		$query = $this->aauth_db->where('receiver_id', $user_id);
		$query = $this->aauth_db->or_where('sender_id', $user_id);
		$query = $this->aauth_db->group_end();
		$query = $this->aauth_db->get( $this->config_vars['pms'] );
		$result = $query->row();
		if ($user_id == $result->sender_id){
			if($result->pm_deleted_receiver == 1){
				return $this->aauth_db->delete( $this->config_vars['pms'], array('id' => $pm_id));			
			}
			
			return $this->aauth_db->update( $this->config_vars['pms'], array('pm_deleted_sender'=>1), array('id' => $pm_id));			
		}else if ($user_id == $result->receiver_id){
			if($result->pm_deleted_sender == 1){
				return $this->aauth_db->delete( $this->config_vars['pms'], array('id' => $pm_id));			
			}

			return $this->aauth_db->update( $this->config_vars['pms'], array('pm_deleted_receiver'=>1, 'date_read'=>date('Y-m-d H:i:s')), array('id' => $pm_id) );
		}
	}

	/**
	 * Cleanup PMs 
	 * Removes PMs older than 'pm_cleanup_max_age' (definied in aauth config).
	 * recommend for a cron job
	 */
	public function cleanup_pms(){
		$pm_cleanup_max_age = $this->config_vars['pm_cleanup_max_age'];
		$date_sent = date('Y-m-d H:i:s', strtotime("now -".$pm_cleanup_max_age));
		$this->aauth_db->where('date_sent <', $date_sent);

		return $this->aauth_db->delete($this->config_vars['pms']);			
	}

	//tested
	/**
	 * Count unread Private Message
	 * Count number of unread private messages
	 * @param int|bool $receiver_id User id for message receiver, if FALSE returns for current user
	 * @return int Number of unread messages
	 */
	public function count_unread_pms($receiver_id=FALSE){

		if(!$receiver_id){
			$receiver_id = $this->CI->session->userdata('id');
		}

		$query = $this->aauth_db->where('receiver_id', $receiver_id);
		$query = $this->aauth_db->where('date_read', NULL);
		$query = $this->aauth_db->where('pm_deleted_sender', NULL);
		$query = $this->aauth_db->where('pm_deleted_receiver', NULL);
		$query = $this->aauth_db->get( $this->config_vars['pms'] );

		return $query->num_rows();
	}

	//tested
	/**
	 * Set Private Message as read
	 * Set private message as read
	 * @param int $pm_id Private message id to mark as read
	 */
	public function set_as_read_pm($pm_id){

		$data = array(
			'date_read' => date('Y-m-d H:i:s')
		);

		$this->aauth_db->update( $this->config_vars['pms'], $data, "id = $pm_id");
	}

	########################
	# Error / Info Functions
	########################

	/**
	 * Error
	 * Add message to error array and set flash data
	 * @param string $message Message to add to array
	 * @param boolean $flashdata if TRUE add $message to CI flashdata (deflault: FALSE)
	 */
	public function error($message = '', $flashdata = FALSE){
		$this->errors[] = $message;
		if($flashdata)
		{
			$this->flash_errors[] = $message;
			$this->CI->session->set_flashdata('errors', $this->flash_errors);
		}
	}

	/**
	 * Keep Errors
	 *
	 * Keeps the flashdata errors for one more page refresh.  Optionally adds the default errors into the
	 * flashdata list.  This should be called last in your controller, and with care as it could continue
	 * to revive all errors and not let them expire as intended.
	 * Benefitial when using Ajax Requests
	 * @see http://ellislab.com/codeigniter/user-guide/libraries/sessions.html
	 * @param boolean $include_non_flash TRUE if it should stow basic errors as flashdata (default = FALSE)
	 */
	public function keep_errors($include_non_flash = FALSE)
	{
		// NOTE: keep_flashdata() overwrites anything new that has been added to flashdata so we are manually reviving flash data
		// $this->CI->session->keep_flashdata('errors');

		if($include_non_flash)
		{
			$this->flash_errors = array_merge($this->flash_errors, $this->errors);
		}
		$this->flash_errors = array_merge($this->flash_errors, (array)$this->CI->session->flashdata('errors'));
		$this->CI->session->set_flashdata('errors', $this->flash_errors);
	}

	//tested
	/**
	 * Get Errors Array
	 * Return array of errors
	 * @return array Array of messages, empty array if no errors
	 */
	public function get_errors_array()
	{
		return $this->errors;
	}

	/**
	 * Print Errors
	 * 
	 * Prints string of errors separated by delimiter
	 * @param string $divider Separator for errors
	 */
	public function print_errors($divider = '<br />')
	{
		$msg = '';
		$msg_num = count($this->errors);
		$i = 1;
		foreach ($this->errors as $e)
		{
			$msg .= $e;

			if ($i != $msg_num)
			{
				$msg .= $divider;
			}
			$i++;
		}
		return $msg;
	}
	
	/**
	 * Clear Errors
	 * 
	 * Removes errors from error list and clears all associated flashdata
	 */
	public function clear_errors()
	{
		$this->errors = array();
		$this->CI->session->set_flashdata('errors', $this->errors);
	}

	/**
	 * Info
	 *
	 * Add message to info array and set flash data
	 * 
	 * @param string $message Message to add to infos array
	 * @param boolean $flashdata if TRUE add $message to CI flashdata (deflault: FALSE)
	 */
	public function info($message = '', $flashdata = FALSE)
	{
		$this->infos[] = $message;
		if($flashdata)
		{
			$this->flash_infos[] = $message;
			$this->CI->session->set_flashdata('infos', $this->flash_infos);
		}
	}

	/**
	 * Keep Infos
	 *
	 * Keeps the flashdata infos for one more page refresh.  Optionally adds the default infos into the
	 * flashdata list.  This should be called last in your controller, and with care as it could continue
	 * to revive all infos and not let them expire as intended.
	 * Benefitial by using Ajax Requests
	 * @see http://ellislab.com/codeigniter/user-guide/libraries/sessions.html
	 * @param boolean $include_non_flash TRUE if it should stow basic infos as flashdata (default = FALSE)
	 */
	public function keep_infos($include_non_flash = FALSE)
	{
		// NOTE: keep_flashdata() overwrites anything new that has been added to flashdata so we are manually reviving flash data
		// $this->CI->session->keep_flashdata('infos');

		if($include_non_flash)
		{
			$this->flash_infos = array_merge($this->flash_infos, $this->infos);
		}
		$this->flash_infos = array_merge($this->flash_infos, (array)$this->CI->session->flashdata('infos'));
		$this->CI->session->set_flashdata('infos', $this->flash_infos);
	}

	/**
	 * Get Info Array
	 *
	 * Return array of infos
	 * @return array Array of messages, empty array if no errors
	 */
	public function get_infos_array()
	{
		return $this->infos;
	}


	/**
	 * Print Info
	 *
	 * Print string of info separated by delimiter
	 * @param string $divider Separator for info
	 *
	 */
	public function print_infos($divider = '<br />')
	{

		$msg = '';
		$msg_num = count($this->infos);
		$i = 1;
		foreach ($this->infos as $e)
		{
			$msg .= $e;

			if ($i != $msg_num)
			{
				$msg .= $divider;
			}
			$i++;
		}
		echo $msg;
	}
	
	/**
	 * Clear Info List
	 * 
	 * Removes info messages from info list and clears all associated flashdata
	 */
	public function clear_infos()
	{
		$this->infos = array();
		$this->CI->session->set_flashdata('infos', $this->infos);
	}

	########################
	# User Variables
	########################

	//tested
	/**
	 * Set User Variable as key value
	 * if variable not set before, it will ve set
	 * if set, overwrites the value
	 * @param string $key
	 * @param string $value
	 * @param int $user_id ; if not given current user
	 * @return bool
	 */
	public function set_user_var( $key, $value, $user_id = FALSE ) {

		if ( ! $user_id ){
			$user_id = $this->CI->session->userdata('id');
		}

		// if specified user is not found
		if ( ! $this->get_user($user_id)){
			return FALSE;
		}

		// if var not set, set
		 if ($this->get_user_var($key,$user_id) ===FALSE) {

			$data = array(
				'data_key' => $key,
				'value' => $value,
				'user_id' => $user_id
			);

			return $this->aauth_db->insert( $this->config_vars['user_variables'] , $data);
		}
		// if var already set, overwrite
		else {

			$data = array(
				'data_key' => $key,
				'value' => $value,
				'user_id' => $user_id
			);

			$this->aauth_db->where( 'data_key', $key );
			$this->aauth_db->where( 'user_id', $user_id);

			return $this->aauth_db->update( $this->config_vars['user_variables'], $data);
		}
	}

	//tested
	/**
	 * Unset User Variable as key value
	 * @param string $key
	 * @param int $user_id ; if not given current user
	 * @return bool
	 */
	public function unset_user_var( $key, $user_id = FALSE ) {

		if ( ! $user_id ){
			$user_id = $this->CI->session->userdata('id');
		}

		// if specified user is not found
		if ( ! $this->get_user($user_id)){
			return FALSE;
		}

		$this->aauth_db->where('data_key', $key);
		$this->aauth_db->where('user_id', $user_id);

		return $this->aauth_db->delete( $this->config_vars['user_variables'] );
	}

	//tested
	/**
	 * Get User Variable by key
	 * Return string of variable value or FALSE
	 * @param string $key
	 * @param int $user_id ; if not given current user
	 * @return bool|string , FALSE if var is not set, the value of var if set
	 */
	public function get_user_var( $key, $user_id = FALSE){

		if ( ! $user_id ){
			$user_id = $this->CI->session->userdata('id');
		}

		// if specified user is not found
		if ( ! $this->get_user($user_id)){
			return FALSE;
		}

		$query = $this->aauth_db->where('user_id', $user_id);
		$query = $this->aauth_db->where('data_key', $key);

		$query = $this->aauth_db->get( $this->config_vars['user_variables'] );

		// if variable not set
		if ($query->num_rows() < 1) { return FALSE;}

		else {

			$row = $query->row();
			return $row->value;
		}

	}
	
   
    /**
	 * Get User Variables by user id
	 * Return array with all user keys & variables
	 * @param int $user_id ; if not given current user
	 * @return bool|array , FALSE if var is not set, the value of var if set
	 */
	public function get_user_vars( $user_id = FALSE){

		if ( ! $user_id ){
			$user_id = $this->CI->session->userdata('id');
		}

		// if specified user is not found
		if ( ! $this->get_user($user_id)){
			return FALSE;
		}

		$query = $this->aauth_db->select('data_key, value');

		$query = $this->aauth_db->where('user_id', $user_id);

		$query = $this->aauth_db->get( $this->config_vars['user_variables'] );

		return $query->result();

	}

	/**
	 * List User Variable Keys by UserID
	 * Return array of variable keys or FALSE
	 * @param int $user_id ; if not given current user
	 * @return bool|array, FALSE if no user vars, otherwise array
	 */
	public function list_user_var_keys($user_id = FALSE){

		if ( ! $user_id ){
			$user_id = $this->CI->session->userdata('id');
		}

		// if specified user is not found
		if ( ! $this->get_user($user_id)){
			return FALSE;
		}
		$query = $this->aauth_db->select('data_key');

		$query = $this->aauth_db->where('user_id', $user_id);

		$query = $this->aauth_db->get( $this->config_vars['user_variables'] );

		// if variable not set
		if ($query->num_rows() < 1) { return FALSE;}
		else {
			return $query->result();
		}
	}

	public function generate_recaptcha_field(){
		$content = '';
		if($this->config_vars['ddos_protection'] && $this->config_vars['recaptcha_active'] && $this->get_login_attempts() >= $this->config_vars['recaptcha_login_attempts']){
			$content .= "<script type='text/javascript' src='https://www.google.com/recaptcha/api.js'></script>";
			$siteKey = $this->config_vars['recaptcha_siteKey'];
			$content .= "<div class='g-recaptcha' data-sitekey='{$siteKey}'></div>";
		}
		return $content;
	}

	public function update_user_totp_secret($secret, $user_id = FALSE) {
		if ($user_id === FALSE) {
			$user_id = $this->CI->session->userdata('id');
		}
	
		$data['totp_secret'] = $secret;
	
		$this->aauth_db->where('id', $user_id);
		return $this->aauth_db->update($this->config_vars['users'], $data);
	}

	public function generate_unique_totp_secret(){
		$this->CI->load->helper('googleauthenticator');
		$ga = new PHPGangsta_GoogleAuthenticator();
		$stop = false;
		while (!$stop) {
			$secret = $ga->createSecret();
			$query = $this->aauth_db->where('totp_secret', $secret);
			$query = $this->aauth_db->get($this->config_vars['users']);
			if ($query->num_rows() == 0) {
				return $secret;
				$stop = true;
			}
		}
	}

	public function generate_totp_qrcode($secret){
		$this->CI->load->helper('googleauthenticator');
		$ga = new PHPGangsta_GoogleAuthenticator();
		return $ga->getQRCodeGoogleUrl($this->config_vars['name'], $secret);
	}

	public function verify_user_totp_code($totp_code, $user_id = FALSE){
		if ( !$this->is_totp_required()) {
			return TRUE;
		}
		if ($user_id == FALSE) {
			$user_id = $this->CI->session->userdata('id');
		}
		if (empty($totp_code)) {
			$this->error($this->CI->lang->line('aauth_error_totp_code_required'));
			return FALSE;
		}
		$query = $this->aauth_db->where('id', $user_id);
		$query = $this->aauth_db->get($this->config_vars['users']);
		$totp_secret =  $query->row()->totp_secret;
		$this->CI->load->helper('googleauthenticator');
		$ga = new PHPGangsta_GoogleAuthenticator();
		$checkResult = $ga->verifyCode($totp_secret, $totp_code, 0);
		if (!$checkResult) {
			$this->error($this->CI->lang->line('aauth_error_totp_code_invalid'));
			return FALSE;
		}else{
			$this->CI->session->unset_userdata('totp_required');
			return TRUE;
		}
	}

	public function is_totp_required(){
		if ( !$this->CI->session->userdata('totp_required')) {
			return FALSE;
		}else if ( $this->CI->session->userdata('totp_required')) {
			return TRUE;
		}
	}

} // end class

// $this->CI->session->userdata('id')

/* coming with v3
----------------
 * captcha (hmm bi bakalım)
 * parametre olarak array alma
 * stacoverflow
 * public id sini 0 a eşitleyip öyle kontrol yapabilirdik (oni boşver uşağum)
 * lock_user (until parametrsi)
 * unlock_user
 * send_pm() in errounda receiver ve sender için ayrı errorlar olabilür
 * ddos protect olayını daha mantıklı hale getür
 * geçici ban ve e-mail ile tkrar aktifleştime olayı
*/

/**
 * Coming with v2
 * -------------
 *
 * tmam // permission id yi permission parametre yap
 * mail fonksiyonları imtihanı
 * tamam // login e ip aderesi de eklemek lazım
 * list_users da grup_par verilirse ve adamın birden fazla grubu varsa nolurkun? // bi denemek lazım belki distinct ile düzelir
 * tamam // eğer grup silinmişse kullanıcıları da o gruptan sil (fire)
 * tamam //	 ismember la is admine 2. parametre olarak user id ekle
 * tamam // kepp infos errors die bişey yap ajax requestlerinde silinir errorlar
 * tmam // user variables
 * tamam // sistem variables
 * tmam // user perms
 * tamam gibi // 4mysql index fulltext index??
 * tamam //delete_user dan sonra grup ve perms ler de silinmeli
 * login() içinde login'i doğru şekilde olsa da yine de login attempt artıyo kesin düzeltilecek
 * keep_errors ve keep_infos calismiyor
 *
 *
 *
 * -----------
 * ok
 *
 * unban_user() added // unlock_user
 * remove member added // fire_member
 * allow() changed to allow_group
 * deny() changed to deny_group
 * is member a yeni parametre eklendi
 * allow_user() added
 * deny_user() added
 * keep_infos() added
 * kepp_errors() added
 * get_errors() changed to print_errors()
 * get_infos() changed to print_infos()
 * User and Aauth System Variables.
set_user_var( $key, $value, $user_id = FALSE )
get_user_var( $key, $user_id = FALSE)
unset
set_system_var( $key, $value, $user_id = FALSE )
get_system_var( $key, $user_id = FALSE)
unset
functions added
 *
 *
 *
 *
 *
 * Done staff v1
 * -----------
 * tamam hacı // control die bi fonksiyon yazıp adam önce login omuşmu sonra da yetkisi var mı die kontrol et. yetkisi yoksa yönlendir ve aktivitiyi güncelle
 * tamam hacı // grupları yetkilendirme, yetki ekleme, alma alow deny
 * tamam gibi // Email and pass validation with form helper
 * biraz oldu // laguage file support
 * tamam // forget pass
 * tamam // yetkilendirme sistemi
 * tamam // Login e remember eklencek
 * tamam // şifremi unuttum ve random string
 * sanırım şimdi tamam // hatalı girişde otomatik süreli kilit
 * ??  tamam heral // mail ile bilgilendirme
 * tamam heral // activasyon emaili
 * tamam gibi // yerine email check // username check
 * tamamlandı // public erişimi
 * tamam // Private messsages
 * tamam össen // errorlar düzenlenecek hepisiiii
 * tamam ama engelleme ve limit olayı koymadım. // pm için okundu ve göster, sil, engelle? die fonksiyonlar eklencek , gönderilen pmler, alınan pmler, arasındaki pmler,
 * tamm// already existedleri info yap onlar error değil hacım
 *




/*
// if user's email is found
if ($query->num_rows() > 0) {
$row = $query->row();

// DDos protection
if ( $this->config_vars['dos_protection'] and $row->last_login_attempt != '' and
(strtotime("now") + 30 * $this->config_vars['try'] ) < strtotime($row->last_login_attempt) ) {
$this->error($this->CI->lang->line('exceeded'));
return FALSE;
}
}
 */



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