PageRenderTime 88ms CodeModel.GetById 11ms app.highlight 61ms RepoModel.GetById 1ms app.codeStats 1ms

/application/models/ion_auth_model.php

https://bitbucket.org/alexdeoliveira/ignitercms
PHP | 2028 lines | 1187 code | 355 blank | 486 comment | 162 complexity | ee6ae6bb2d98e9e1a673fcbfd7c5c1dc MD5 | raw file
   1<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
   2/**
   3* Name:  Ion Auth Model
   4*
   5* Author:  Ben Edmunds
   6* 		   ben.edmunds@gmail.com
   7*	  	   @benedmunds
   8*
   9* Added Awesomeness: Phil Sturgeon
  10*
  11* Location: http://github.com/benedmunds/CodeIgniter-Ion-Auth
  12*
  13* Created:  10.01.2009
  14*
  15* Description:  Modified auth system based on redux_auth with extensive customization.  This is basically what Redux Auth 2 should be.
  16* Original Author name has been kept but that does not mean that the method has not been modified.
  17*
  18* Requirements: PHP5 or above
  19*
  20*/
  21
  22class Ion_auth_model extends CI_Model
  23{
  24	/**
  25	 * Holds an array of tables used
  26	 *
  27	 * @var string
  28	 **/
  29	public $tables = array();
  30
  31	/**
  32	 * activation code
  33	 *
  34	 * @var string
  35	 **/
  36	public $activation_code;
  37
  38	/**
  39	 * forgotten password key
  40	 *
  41	 * @var string
  42	 **/
  43	public $forgotten_password_code;
  44
  45	/**
  46	 * new password
  47	 *
  48	 * @var string
  49	 **/
  50	public $new_password;
  51
  52	/**
  53	 * Identity
  54	 *
  55	 * @var string
  56	 **/
  57	public $identity;
  58
  59	/**
  60	 * Where
  61	 *
  62	 * @var array
  63	 **/
  64	public $_ion_where = array();
  65
  66	/**
  67	 * Select
  68	 *
  69	 * @var string
  70	 **/
  71	public $_ion_select = array();
  72
  73	/**
  74	 * Like
  75	 *
  76	 * @var string
  77	 **/
  78	public $_ion_like = array();
  79
  80	/**
  81	 * Limit
  82	 *
  83	 * @var string
  84	 **/
  85	public $_ion_limit = NULL;
  86
  87	/**
  88	 * Offset
  89	 *
  90	 * @var string
  91	 **/
  92	public $_ion_offset = NULL;
  93
  94	/**
  95	 * Order By
  96	 *
  97	 * @var string
  98	 **/
  99	public $_ion_order_by = NULL;
 100
 101	/**
 102	 * Order
 103	 *
 104	 * @var string
 105	 **/
 106	public $_ion_order = NULL;
 107
 108	/**
 109	 * Hooks
 110	 *
 111	 * @var object
 112	 **/
 113	protected $_ion_hooks;
 114
 115	/**
 116	 * Response
 117	 *
 118	 * @var string
 119	 **/
 120	protected $response = NULL;
 121
 122	/**
 123	 * message (uses lang file)
 124	 *
 125	 * @var string
 126	 **/
 127	protected $messages;
 128
 129	/**
 130	 * error message (uses lang file)
 131	 *
 132	 * @var string
 133	 **/
 134	protected $errors;
 135
 136	/**
 137	 * error start delimiter
 138	 *
 139	 * @var string
 140	 **/
 141	protected $error_start_delimiter;
 142
 143	/**
 144	 * error end delimiter
 145	 *
 146	 * @var string
 147	 **/
 148	protected $error_end_delimiter;
 149
 150	/**
 151	 * caching of users and their groups
 152	 *
 153	 * @var array
 154	 **/
 155	public $_cache_user_in_group = array();
 156
 157	/**
 158	 * caching of groups
 159	 *
 160	 * @var array
 161	 **/
 162	protected $_cache_groups = array();
 163
 164	public function __construct()
 165	{
 166		parent::__construct();
 167		$this->load->database();
 168		$this->load->config('ion_auth', TRUE);
 169		$this->load->helper('cookie');
 170		$this->load->helper('date');
 171		$this->load->library('session');
 172		$this->lang->load('ion_auth');
 173
 174		//initialize db tables data
 175		$this->tables  = $this->config->item('tables', 'ion_auth');
 176
 177		//initialize data
 178		$this->identity_column = $this->config->item('identity', 'ion_auth');
 179		$this->store_salt      = $this->config->item('store_salt', 'ion_auth');
 180		$this->salt_length     = $this->config->item('salt_length', 'ion_auth');
 181		$this->join			   = $this->config->item('join', 'ion_auth');
 182
 183
 184		//initialize hash method options (Bcrypt)
 185		$this->hash_method = $this->config->item('hash_method', 'ion_auth');
 186		$this->default_rounds = $this->config->item('default_rounds', 'ion_auth');
 187		$this->random_rounds = $this->config->item('random_rounds', 'ion_auth');
 188		$this->min_rounds = $this->config->item('min_rounds', 'ion_auth');
 189		$this->max_rounds = $this->config->item('max_rounds', 'ion_auth');
 190
 191
 192		//initialize messages and error
 193		$this->messages = array();
 194		$this->errors = array();
 195		$this->message_start_delimiter = $this->config->item('message_start_delimiter', 'ion_auth');
 196		$this->message_end_delimiter   = $this->config->item('message_end_delimiter', 'ion_auth');
 197		$this->error_start_delimiter   = $this->config->item('error_start_delimiter', 'ion_auth');
 198		$this->error_end_delimiter     = $this->config->item('error_end_delimiter', 'ion_auth');
 199
 200		//initialize our hooks object
 201		$this->_ion_hooks = new stdClass;
 202
 203		//load the bcrypt class if needed
 204		if ($this->hash_method == 'bcrypt') {
 205			if ($this->random_rounds)
 206			{
 207				$rand = rand($this->min_rounds,$this->max_rounds);
 208				$rounds = array('rounds' => $rand);
 209			}
 210			else
 211			{
 212				$rounds = array('rounds' => $this->default_rounds);
 213			}
 214
 215			$this->load->library('bcrypt',$rounds);
 216		}
 217
 218		$this->trigger_events('model_constructor');
 219	}
 220
 221	/**
 222	 * Misc functions
 223	 *
 224	 * Hash password : Hashes the password to be stored in the database.
 225	 * Hash password db : This function takes a password and validates it
 226	 * against an entry in the users table.
 227	 * Salt : Generates a random salt value.
 228	 *
 229	 * @author Mathew
 230	 */
 231
 232	/**
 233	 * Hashes the password to be stored in the database.
 234	 *
 235	 * @return void
 236	 * @author Mathew
 237	 **/
 238	public function hash_password($password, $salt=false, $use_sha1_override=FALSE)
 239	{
 240		if (empty($password))
 241		{
 242			return FALSE;
 243		}
 244
 245		//bcrypt
 246		if ($use_sha1_override === FALSE && $this->hash_method == 'bcrypt')
 247		{
 248			return $this->bcrypt->hash($password);
 249		}
 250
 251
 252		if ($this->store_salt && $salt)
 253		{
 254			return  sha1($password . $salt);
 255		}
 256		else
 257		{
 258			$salt = $this->salt();
 259			return  $salt . substr(sha1($salt . $password), 0, -$this->salt_length);
 260		}
 261	}
 262
 263	/**
 264	 * This function takes a password and validates it
 265	 * against an entry in the users table.
 266	 *
 267	 * @return void
 268	 * @author Mathew
 269	 **/
 270	public function hash_password_db($id, $password, $use_sha1_override=FALSE)
 271	{
 272		if (empty($id) || empty($password))
 273		{
 274			return FALSE;
 275		}
 276
 277		$this->trigger_events('extra_where');
 278
 279		$query = $this->db->select('password, salt')
 280		                  ->where('id', $id)
 281		                  ->limit(1)
 282		                  ->get($this->tables['users']);
 283
 284		$hash_password_db = $query->row();
 285
 286		if ($query->num_rows() !== 1)
 287		{
 288			return FALSE;
 289		}
 290
 291		// bcrypt
 292		if ($use_sha1_override === FALSE && $this->hash_method == 'bcrypt')
 293		{
 294			if ($this->bcrypt->verify($password,$hash_password_db->password))
 295			{
 296				return TRUE;
 297			}
 298
 299			return FALSE;
 300		}
 301
 302		// sha1
 303		if ($this->store_salt)
 304		{
 305			$db_password = sha1($password . $hash_password_db->salt);
 306		}
 307		else
 308		{
 309			$salt = substr($hash_password_db->password, 0, $this->salt_length);
 310
 311			$db_password =  $salt . substr(sha1($salt . $password), 0, -$this->salt_length);
 312		}
 313
 314		if($db_password == $hash_password_db->password)
 315		{
 316			return TRUE;
 317		}
 318		else
 319		{
 320			return FALSE;
 321		}
 322	}
 323
 324	/**
 325	 * Generates a random salt value for forgotten passwords or any other keys. Uses SHA1.
 326	 *
 327	 * @return void
 328	 * @author Mathew
 329	 **/
 330	public function hash_code($password)
 331	{
 332		return $this->hash_password($password, FALSE, TRUE);
 333	}
 334
 335	/**
 336	 * Generates a random salt value.
 337	 *
 338	 * @return void
 339	 * @author Mathew
 340	 **/
 341	public function salt()
 342	{
 343		return substr(md5(uniqid(rand(), true)), 0, $this->salt_length);
 344	}
 345
 346	/**
 347	 * Activation functions
 348	 *
 349	 * Activate : Validates and removes activation code.
 350	 * Deactivae : Updates a users row with an activation code.
 351	 *
 352	 * @author Mathew
 353	 */
 354
 355	/**
 356	 * activate
 357	 *
 358	 * @return void
 359	 * @author Mathew
 360	 **/
 361	public function activate($id, $code = false)
 362	{
 363		$this->trigger_events('pre_activate');
 364
 365		if ($code !== FALSE)
 366		{
 367			$query = $this->db->select($this->identity_column)
 368			                  ->where('activation_code', $code)
 369			                  ->limit(1)
 370			                  ->get($this->tables['users']);
 371
 372			$result = $query->row();
 373
 374			if ($query->num_rows() !== 1)
 375			{
 376				$this->trigger_events(array('post_activate', 'post_activate_unsuccessful'));
 377				$this->set_error('activate_unsuccessful');
 378				return FALSE;
 379			}
 380
 381			$identity = $result->{$this->identity_column};
 382
 383			$data = array(
 384			    'activation_code' => NULL,
 385			    'active'          => 1
 386			);
 387
 388			$this->trigger_events('extra_where');
 389			$this->db->update($this->tables['users'], $data, array($this->identity_column => $identity));
 390		}
 391		else
 392		{
 393			$data = array(
 394			    'activation_code' => NULL,
 395			    'active'          => 1
 396			);
 397
 398
 399			$this->trigger_events('extra_where');
 400			$this->db->update($this->tables['users'], $data, array('id' => $id));
 401		}
 402
 403
 404		$return = $this->db->affected_rows() == 1;
 405		if ($return)
 406		{
 407			$this->trigger_events(array('post_activate', 'post_activate_successful'));
 408			$this->set_message('activate_successful');
 409		}
 410		else
 411		{
 412			$this->trigger_events(array('post_activate', 'post_activate_unsuccessful'));
 413			$this->set_error('activate_unsuccessful');
 414		}
 415
 416
 417		return $return;
 418	}
 419
 420
 421	/**
 422	 * Deactivate
 423	 *
 424	 * @return void
 425	 * @author Mathew
 426	 **/
 427	public function deactivate($id = NULL)
 428	{
 429		$this->trigger_events('deactivate');
 430
 431		if (!isset($id))
 432		{
 433			$this->set_error('deactivate_unsuccessful');
 434			return FALSE;
 435		}
 436
 437		$activation_code       = sha1(md5(microtime()));
 438		$this->activation_code = $activation_code;
 439
 440		$data = array(
 441		    'activation_code' => $activation_code,
 442		    'active'          => 0
 443		);
 444
 445		$this->trigger_events('extra_where');
 446		$this->db->update($this->tables['users'], $data, array('id' => $id));
 447
 448		$return = $this->db->affected_rows() == 1;
 449		if ($return)
 450			$this->set_message('deactivate_successful');
 451		else
 452			$this->set_error('deactivate_unsuccessful');
 453
 454		return $return;
 455	}
 456
 457	public function clear_forgotten_password_code($code) {
 458
 459		if (empty($code))
 460		{
 461			return FALSE;
 462		}
 463
 464		$this->db->where('forgotten_password_code', $code);
 465
 466		if ($this->db->count_all_results($this->tables['users']) > 0)
 467		{
 468			$data = array(
 469			    'forgotten_password_code' => NULL,
 470			    'forgotten_password_time' => NULL
 471			);
 472
 473			$this->db->update($this->tables['users'], $data, array('forgotten_password_code' => $code));
 474
 475			return TRUE;
 476		}
 477
 478		return FALSE;
 479	}
 480
 481	/**
 482	 * reset password
 483	 *
 484	 * @return bool
 485	 * @author Mathew
 486	 **/
 487	public function reset_password($identity, $new) {
 488		$this->trigger_events('pre_change_password');
 489
 490		if (!$this->identity_check($identity)) {
 491			$this->trigger_events(array('post_change_password', 'post_change_password_unsuccessful'));
 492			return FALSE;
 493		}
 494
 495		$this->trigger_events('extra_where');
 496
 497		$query = $this->db->select('id, password, salt')
 498		                  ->where($this->identity_column, $identity)
 499		                  ->limit(1)
 500		                  ->get($this->tables['users']);
 501
 502		if ($query->num_rows() !== 1)
 503		{
 504			$this->trigger_events(array('post_change_password', 'post_change_password_unsuccessful'));
 505			$this->set_error('password_change_unsuccessful');
 506			return FALSE;
 507		}
 508
 509		$result = $query->row();
 510
 511		$new = $this->hash_password($new, $result->salt);
 512
 513		//store the new password and reset the remember code so all remembered instances have to re-login
 514		//also clear the forgotten password code
 515		$data = array(
 516		    'password' => $new,
 517		    'remember_code' => NULL,
 518		    'forgotten_password_code' => NULL,
 519		    'forgotten_password_time' => NULL,
 520		);
 521
 522		$this->trigger_events('extra_where');
 523		$this->db->update($this->tables['users'], $data, array($this->identity_column => $identity));
 524
 525		$return = $this->db->affected_rows() == 1;
 526		if ($return)
 527		{
 528			$this->trigger_events(array('post_change_password', 'post_change_password_successful'));
 529			$this->set_message('password_change_successful');
 530		}
 531		else
 532		{
 533			$this->trigger_events(array('post_change_password', 'post_change_password_unsuccessful'));
 534			$this->set_error('password_change_unsuccessful');
 535		}
 536
 537		return $return;
 538	}
 539
 540	/**
 541	 * change password
 542	 *
 543	 * @return bool
 544	 * @author Mathew
 545	 **/
 546	public function change_password($identity, $old, $new)
 547	{
 548		$this->trigger_events('pre_change_password');
 549
 550		$this->trigger_events('extra_where');
 551
 552		$query = $this->db->select('id, password, salt')
 553		                  ->where($this->identity_column, $identity)
 554		                  ->limit(1)
 555		                  ->get($this->tables['users']);
 556
 557		if ($query->num_rows() !== 1)
 558		{
 559			$this->trigger_events(array('post_change_password', 'post_change_password_unsuccessful'));
 560			$this->set_error('password_change_unsuccessful');
 561			return FALSE;
 562		}
 563
 564		$result = $query->row();
 565
 566		$db_password = $result->password;
 567		$old         = $this->hash_password_db($result->id, $old);
 568		$new         = $this->hash_password($new, $result->salt);
 569
 570		if ($old === TRUE)
 571		{
 572			//store the new password and reset the remember code so all remembered instances have to re-login
 573			$data = array(
 574			    'password' => $new,
 575			    'remember_code' => NULL,
 576			);
 577
 578			$this->trigger_events('extra_where');
 579			$this->db->update($this->tables['users'], $data, array($this->identity_column => $identity));
 580
 581			$return = $this->db->affected_rows() == 1;
 582			if ($return)
 583			{
 584				$this->trigger_events(array('post_change_password', 'post_change_password_successful'));
 585				$this->set_message('password_change_successful');
 586			}
 587			else
 588			{
 589				$this->trigger_events(array('post_change_password', 'post_change_password_unsuccessful'));
 590				$this->set_error('password_change_unsuccessful');
 591			}
 592
 593			return $return;
 594		}
 595
 596		$this->set_error('password_change_unsuccessful');
 597		return FALSE;
 598	}
 599
 600	/**
 601	 * Checks username
 602	 *
 603	 * @return bool
 604	 * @author Mathew
 605	 **/
 606	public function username_check($username = '')
 607	{
 608		$this->trigger_events('username_check');
 609
 610		if (empty($username))
 611		{
 612			return FALSE;
 613		}
 614
 615		$this->trigger_events('extra_where');
 616
 617		return $this->db->where('username', $username)
 618		                ->count_all_results($this->tables['users']) > 0;
 619	}
 620
 621	/**
 622	 * Checks email
 623	 *
 624	 * @return bool
 625	 * @author Mathew
 626	 **/
 627	public function email_check($email = '')
 628	{
 629		$this->trigger_events('email_check');
 630
 631		if (empty($email))
 632		{
 633			return FALSE;
 634		}
 635
 636		$this->trigger_events('extra_where');
 637
 638		return $this->db->where('email', $email)
 639		                ->count_all_results($this->tables['users']) > 0;
 640	}
 641
 642	/**
 643	 * Identity check
 644	 *
 645	 * @return bool
 646	 * @author Mathew
 647	 **/
 648	public function identity_check($identity = '')
 649	{
 650		$this->trigger_events('identity_check');
 651
 652		if (empty($identity))
 653		{
 654			return FALSE;
 655		}
 656
 657		return $this->db->where($this->identity_column, $identity)
 658		                ->count_all_results($this->tables['users']) > 0;
 659	}
 660
 661	/**
 662	 * Insert a forgotten password key.
 663	 *
 664	 * @return bool
 665	 * @author Mathew
 666	 * @updated Ryan
 667	 **/
 668	public function forgotten_password($identity)
 669	{
 670		if (empty($identity))
 671		{
 672			$this->trigger_events(array('post_forgotten_password', 'post_forgotten_password_unsuccessful'));
 673			return FALSE;
 674		}
 675
 676		$key = $this->hash_code(microtime().$identity);
 677
 678		$this->forgotten_password_code = $key;
 679
 680		$this->trigger_events('extra_where');
 681
 682		$update = array(
 683		    'forgotten_password_code' => $key,
 684		    'forgotten_password_time' => time()
 685		);
 686
 687		$this->db->update($this->tables['users'], $update, array($this->identity_column => $identity));
 688
 689		$return = $this->db->affected_rows() == 1;
 690
 691		if ($return)
 692			$this->trigger_events(array('post_forgotten_password', 'post_forgotten_password_successful'));
 693		else
 694			$this->trigger_events(array('post_forgotten_password', 'post_forgotten_password_unsuccessful'));
 695
 696		return $return;
 697	}
 698
 699	/**
 700	 * Forgotten Password Complete
 701	 *
 702	 * @return string
 703	 * @author Mathew
 704	 **/
 705	public function forgotten_password_complete($code, $salt=FALSE)
 706	{
 707		$this->trigger_events('pre_forgotten_password_complete');
 708
 709		if (empty($code))
 710		{
 711			$this->trigger_events(array('post_forgotten_password_complete', 'post_forgotten_password_complete_unsuccessful'));
 712			return FALSE;
 713		}
 714
 715		$profile = $this->where('forgotten_password_code', $code)->users()->row(); //pass the code to profile
 716
 717		if ($profile) {
 718
 719			if ($this->config->item('forgot_password_expiration', 'ion_auth') > 0) {
 720				//Make sure it isn't expired
 721				$expiration = $this->config->item('forgot_password_expiration', 'ion_auth');
 722				if (time() - $profile->forgotten_password_time > $expiration) {
 723					//it has expired
 724					$this->set_error('forgot_password_expired');
 725					$this->trigger_events(array('post_forgotten_password_complete', 'post_forgotten_password_complete_unsuccessful'));
 726					return FALSE;
 727				}
 728			}
 729
 730			$password = $this->salt();
 731
 732			$data = array(
 733			    'password'                => $this->hash_password($password, $salt),
 734			    'forgotten_password_code' => NULL,
 735			    'active'                  => 1,
 736			 );
 737
 738			$this->db->update($this->tables['users'], $data, array('forgotten_password_code' => $code));
 739
 740			$this->trigger_events(array('post_forgotten_password_complete', 'post_forgotten_password_complete_successful'));
 741			return $password;
 742		}
 743
 744		$this->trigger_events(array('post_forgotten_password_complete', 'post_forgotten_password_complete_unsuccessful'));
 745		return FALSE;
 746	}
 747
 748	/**
 749	 * register
 750	 *
 751	 * @return bool
 752	 * @author Mathew
 753	 **/
 754	public function register($username, $password, $email, $additional_data = array(), $groups = array())
 755	{
 756		$this->trigger_events('pre_register');
 757
 758		$manual_activation = $this->config->item('manual_activation', 'ion_auth');
 759
 760		if ($this->identity_column == 'email' && $this->email_check($email))
 761		{
 762			$this->set_error('account_creation_duplicate_email');
 763			return FALSE;
 764		}
 765		elseif ($this->identity_column == 'username' && $this->username_check($username))
 766		{
 767			$this->set_error('account_creation_duplicate_username');
 768			return FALSE;
 769		}
 770
 771		// If username is taken, use username1 or username2, etc.
 772		if ($this->identity_column != 'username')
 773		{
 774			$original_username = $username;
 775			for($i = 0; $this->username_check($username); $i++)
 776			{
 777				if($i > 0)
 778				{
 779					$username = $original_username . $i;
 780				}
 781			}
 782		}
 783
 784		// IP Address
 785		$ip_address = $this->_prepare_ip($this->input->ip_address());
 786		$salt       = $this->store_salt ? $this->salt() : FALSE;
 787		$password   = $this->hash_password($password, $salt);
 788
 789		// Users table.
 790		$data = array(
 791		    'username'   => $username,
 792		    'password'   => $password,
 793		    'email'      => $email,
 794		    'ip_address' => $ip_address,
 795		    'created_on' => time(),
 796		    'last_login' => time(),
 797		    'active'     => ($manual_activation === false ? 1 : 0)
 798		);
 799
 800		if ($this->store_salt)
 801		{
 802			$data['salt'] = $salt;
 803		}
 804
 805		//filter out any data passed that doesnt have a matching column in the users table
 806		//and merge the set user data and the additional data
 807		$user_data = array_merge($this->_filter_data($this->tables['users'], $additional_data), $data);
 808
 809		$this->trigger_events('extra_set');
 810
 811		$this->db->insert($this->tables['users'], $user_data);
 812
 813		$id = $this->db->insert_id();
 814
 815		if (!empty($groups))
 816		{
 817			//add to groups
 818			foreach ($groups as $group)
 819			{
 820				$this->add_to_group($group, $id);
 821			}
 822		}
 823
 824		//add to default group if not already set
 825		$default_group = $this->where('name', $this->config->item('default_group', 'ion_auth'))->group()->row();
 826		if ((isset($default_group->id) && !isset($groups)) || (empty($groups) && !in_array($default_group->id, $groups)))
 827		{
 828			$this->add_to_group($default_group->id, $id);
 829		}
 830
 831		$this->trigger_events('post_register');
 832
 833		return (isset($id)) ? $id : FALSE;
 834	}
 835
 836	/**
 837	 * login
 838	 *
 839	 * @return bool
 840	 * @author Mathew
 841	 **/
 842	public function login($identity, $password, $remember=FALSE)
 843	{
 844		$this->trigger_events('pre_login');
 845
 846		if (empty($identity) || empty($password))
 847		{
 848			$this->set_error('login_unsuccessful');
 849			return FALSE;
 850		}
 851
 852
 853		$this->trigger_events('extra_where');
 854
 855		$query = $this->db->select($this->identity_column . ', username, email, id, password, active, last_login, first_name, last_name')
 856		                  ->where($this->identity_column, $this->db->escape_str($identity))
 857		                  ->limit(1)
 858		                  ->get($this->tables['users']);
 859
 860						  
 861		if($this->is_time_locked_out($identity))
 862		{
 863			//Hash something anyway, just to take up time
 864			$this->hash_password($password);
 865			
 866			$this->trigger_events('post_login_unsuccessful');
 867			$this->set_error('login_timeout');
 868
 869			return FALSE;
 870		}
 871
 872		if ($query->num_rows() === 1)
 873		{
 874			$user = $query->row();
 875
 876			$password = $this->hash_password_db($user->id, $password);
 877
 878			if ($password === TRUE)
 879			{
 880				if ($user->active == 0)
 881				{
 882					$this->trigger_events('post_login_unsuccessful');
 883					$this->set_error('login_unsuccessful_not_active');
 884
 885					return FALSE;
 886				}
 887
 888				$session_data = array(
 889				    'identity'             => $user->{$this->identity_column},
 890				    'username'             => $user->username,
 891				    'email'                => $user->email,
 892				    'user_id'              => $user->id, //everyone likes to overwrite id so we'll use user_id
 893				    'old_last_login'       => $user->last_login,
 894				    'first_name'			=> $user->first_name,
 895				    'last_name'				=> $user->last_name,
 896				);
 897
 898				$this->update_last_login($user->id);
 899
 900				$this->clear_login_attempts($identity);
 901
 902				$this->session->set_userdata($session_data);
 903
 904				if ($remember && $this->config->item('remember_users', 'ion_auth'))
 905				{
 906					$this->remember_user($user->id);
 907				}
 908
 909				$this->trigger_events(array('post_login', 'post_login_successful'));
 910				$this->set_message('login_successful');
 911
 912				return TRUE;
 913			}
 914		}
 915
 916		//Hash something anyway, just to take up time
 917		$this->hash_password($password);
 918
 919		$this->increase_login_attempts($identity);
 920
 921		$this->trigger_events('post_login_unsuccessful');
 922		$this->set_error('login_unsuccessful');
 923
 924		return FALSE;
 925	}
 926
 927	/**
 928	 * is_max_login_attempts_exceeded
 929	 * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
 930	 *
 931	 * @param string $identity
 932	 * @return boolean
 933	 **/
 934	public function is_max_login_attempts_exceeded($identity) {
 935		if ($this->config->item('track_login_attempts', 'ion_auth')) {
 936			$max_attempts = $this->config->item('maximum_login_attempts', 'ion_auth');
 937			if ($max_attempts > 0) {
 938				$attempts = $this->get_attempts_num($identity);
 939				return $attempts >= $max_attempts;
 940			}
 941		}
 942		return FALSE;
 943	}
 944
 945	/**
 946	 * Get number of attempts to login occured from given IP-address or identity
 947	 * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
 948	 *
 949	 * @param	string $identity
 950	 * @return	int
 951	 */
 952	function get_attempts_num($identity)
 953	{
 954		if ($this->config->item('track_login_attempts', 'ion_auth')) {
 955			$ip_address = $this->_prepare_ip($this->input->ip_address());
 956
 957			$this->db->select('1', FALSE);
 958			$this->db->where('ip_address', $ip_address);
 959			if (strlen($identity) > 0) $this->db->or_where('login', $identity);
 960
 961			$qres = $this->db->get($this->tables['login_attempts']);
 962			return $qres->num_rows();
 963		}
 964		return 0;
 965	}
 966	
 967	/**
 968	 * Get a boolean to determine if an account should be locked out due to
 969	 * exceeded login attempts within a given period
 970	 *
 971	 * @return	boolean
 972	 */
 973	public function is_time_locked_out($identity) {
 974
 975		return $this->is_max_login_attempts_exceeded($identity) && $this->get_last_attempt_time($identity) > time() - $this->config->item('lockout_time', 'ion_auth');
 976	}
 977	
 978	/**
 979	 * Get the time of the last time a login attempt occured from given IP-address or identity
 980	 *
 981	 * @param	string $identity
 982	 * @return	int
 983	 */
 984	public function get_last_attempt_time($identity) {
 985		if ($this->config->item('track_login_attempts', 'ion_auth')) {
 986			$ip_address = $this->_prepare_ip($this->input->ip_address());
 987			
 988			$this->db->select_max('time');
 989			$this->db->where('ip_address', $ip_address);
 990			if (strlen($identity) > 0) $this->db->or_where('login', $identity);
 991			$qres = $this->db->get($this->tables['login_attempts'], 1);
 992			
 993			if($qres->num_rows() > 0) {
 994				return $qres->row()->time;
 995			}
 996		}
 997		
 998		return 0;
 999	}
1000
1001	/**
1002	 * increase_login_attempts
1003	 * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
1004	 *
1005	 * @param string $identity
1006	 **/
1007	public function increase_login_attempts($identity) {
1008		if ($this->config->item('track_login_attempts', 'ion_auth')) {
1009			$ip_address = $this->_prepare_ip($this->input->ip_address());
1010			return $this->db->insert($this->tables['login_attempts'], array('ip_address' => $ip_address, 'login' => $identity, 'time' => time()));
1011		}
1012		return FALSE;
1013	}
1014
1015	/**
1016	 * clear_login_attempts
1017	 * Based on code from Tank Auth, by Ilya Konyukhov (https://github.com/ilkon/Tank-Auth)
1018	 *
1019	 * @param string $identity
1020	 **/
1021	public function clear_login_attempts($identity, $expire_period = 86400) {
1022		if ($this->config->item('track_login_attempts', 'ion_auth')) {
1023			$ip_address = $this->_prepare_ip($this->input->ip_address());
1024
1025			$this->db->where(array('ip_address' => $ip_address, 'login' => $identity));
1026			// Purge obsolete login attempts
1027			$this->db->or_where('time <', time() - $expire_period, FALSE);
1028
1029			return $this->db->delete($this->tables['login_attempts']);
1030		}
1031		return FALSE;
1032	}
1033
1034	public function limit($limit)
1035	{
1036		$this->trigger_events('limit');
1037		$this->_ion_limit = $limit;
1038
1039		return $this;
1040	}
1041
1042	public function offset($offset)
1043	{
1044		$this->trigger_events('offset');
1045		$this->_ion_offset = $offset;
1046
1047		return $this;
1048	}
1049
1050	public function where($where, $value = NULL)
1051	{
1052		$this->trigger_events('where');
1053
1054		if (!is_array($where))
1055		{
1056			$where = array($where => $value);
1057		}
1058
1059		array_push($this->_ion_where, $where);
1060
1061		return $this;
1062	}
1063
1064	public function like($like, $value = NULL)
1065	{
1066		$this->trigger_events('like');
1067
1068		if (!is_array($like))
1069		{
1070			$like = array($like => $value);
1071		}
1072
1073		array_push($this->_ion_like, $like);
1074
1075		return $this;
1076	}
1077
1078	public function select($select)
1079	{
1080		$this->trigger_events('select');
1081
1082		$this->_ion_select[] = $select;
1083
1084		return $this;
1085	}
1086
1087	public function order_by($by, $order='desc')
1088	{
1089		$this->trigger_events('order_by');
1090
1091		$this->_ion_order_by = $by;
1092		$this->_ion_order    = $order;
1093
1094		return $this;
1095	}
1096
1097	public function row()
1098	{
1099		$this->trigger_events('row');
1100
1101		$row = $this->response->row();
1102		$this->response->free_result();
1103
1104		return $row;
1105	}
1106
1107	public function row_array()
1108	{
1109		$this->trigger_events(array('row', 'row_array'));
1110
1111		$row = $this->response->row_array();
1112		$this->response->free_result();
1113
1114		return $row;
1115	}
1116
1117	public function result()
1118	{
1119		$this->trigger_events('result');
1120
1121		$result = $this->response->result();
1122		$this->response->free_result();
1123
1124		return $result;
1125	}
1126
1127	public function result_array()
1128	{
1129		$this->trigger_events(array('result', 'result_array'));
1130
1131		$result = $this->response->result_array();
1132		$this->response->free_result();
1133
1134		return $result;
1135	}
1136
1137	public function num_rows()
1138	{
1139		$this->trigger_events(array('num_rows'));
1140
1141		$result = $this->response->num_rows();
1142		$this->response->free_result();
1143
1144		return $result;
1145	}
1146
1147	/**
1148	 * users
1149	 *
1150	 * @return object Users
1151	 * @author Ben Edmunds
1152	 **/
1153	public function users($groups = NULL)
1154	{
1155		$this->trigger_events('users');
1156
1157		if (isset($this->_ion_select))
1158		{
1159			foreach ($this->_ion_select as $select)
1160			{
1161				$this->db->select($select);
1162			}
1163
1164			$this->_ion_select = array();
1165		}
1166		else
1167		{
1168			//default selects
1169			$this->db->select(array(
1170			    $this->tables['users'].'.*',
1171			    $this->tables['users'].'.id as id',
1172			    $this->tables['users'].'.id as user_id'
1173			));
1174		}
1175
1176		//filter by group id(s) if passed
1177		if (isset($groups))
1178		{
1179			//build an array if only one group was passed
1180			if (is_numeric($groups))
1181			{
1182				$groups = Array($groups);
1183			}
1184
1185			//join and then run a where_in against the group ids
1186			if (isset($groups) && !empty($groups))
1187			{
1188				$this->db->distinct();
1189				$this->db->join(
1190				    $this->tables['users_groups'],
1191				    $this->tables['users_groups'].'.user_id = ' . $this->tables['users'].'.id',
1192				    'inner'
1193				);
1194
1195				$this->db->where_in($this->tables['users_groups'].'.group_id', $groups);
1196			}
1197		}
1198
1199		$this->trigger_events('extra_where');
1200
1201		//run each where that was passed
1202		if (isset($this->_ion_where))
1203		{
1204			foreach ($this->_ion_where as $where)
1205			{
1206				$this->db->where($where);
1207			}
1208
1209			$this->_ion_where = array();
1210		}
1211
1212		if (isset($this->_ion_like))
1213		{
1214			foreach ($this->_ion_like as $like)
1215			{
1216				$this->db->or_like($like);
1217			}
1218
1219			$this->_ion_like = array();
1220		}
1221
1222		if (isset($this->_ion_limit) && isset($this->_ion_offset))
1223		{
1224			$this->db->limit($this->_ion_limit, $this->_ion_offset);
1225
1226			$this->_ion_limit  = NULL;
1227			$this->_ion_offset = NULL;
1228		}
1229		else if (isset($this->_ion_limit))
1230		{
1231			$this->db->limit($this->_ion_limit);
1232
1233			$this->_ion_limit  = NULL;
1234		}
1235
1236		//set the order
1237		if (isset($this->_ion_order_by) && isset($this->_ion_order))
1238		{
1239			$this->db->order_by($this->_ion_order_by, $this->_ion_order);
1240
1241			$this->_ion_order    = NULL;
1242			$this->_ion_order_by = NULL;
1243		}
1244
1245		$this->response = $this->db->get($this->tables['users']);
1246
1247		return $this;
1248	}
1249
1250	/**
1251	 * user
1252	 *
1253	 * @return object
1254	 * @author Ben Edmunds
1255	 **/
1256	public function user($id = NULL)
1257	{
1258		$this->trigger_events('user');
1259
1260		//if no id was passed use the current users id
1261		$id || $id = $this->session->userdata('user_id');
1262
1263		$this->limit(1);
1264		$this->where($this->tables['users'].'.id', $id);
1265
1266		$this->users();
1267
1268		return $this;
1269	}
1270
1271	/**
1272	 * get_users_groups
1273	 *
1274	 * @return array
1275	 * @author Ben Edmunds
1276	 **/
1277	public function get_users_groups($id=FALSE)
1278	{
1279		$this->trigger_events('get_users_group');
1280
1281		//if no id was passed use the current users id
1282		$id || $id = $this->session->userdata('user_id');
1283
1284		return $this->db->select($this->tables['users_groups'].'.'.$this->join['groups'].' as id, '.$this->tables['groups'].'.name, '.$this->tables['groups'].'.description')
1285		                ->where($this->tables['users_groups'].'.'.$this->join['users'], $id)
1286		                ->join($this->tables['groups'], $this->tables['users_groups'].'.'.$this->join['groups'].'='.$this->tables['groups'].'.id')
1287		                ->get($this->tables['users_groups']);
1288	}
1289
1290	/**
1291	 * add_to_group
1292	 *
1293	 * @return bool
1294	 * @author Ben Edmunds
1295	 **/
1296	public function add_to_group($group_id, $user_id=false)
1297	{
1298		$this->trigger_events('add_to_group');
1299
1300		//if no id was passed use the current users id
1301		$user_id || $user_id = $this->session->userdata('user_id');
1302
1303		if ($return = $this->db->insert($this->tables['users_groups'], array( $this->join['groups'] => (int)$group_id, $this->join['users'] => (int)$user_id)))
1304		{
1305			if (isset($this->_cache_groups[$group_id])) {
1306				$group_name = $this->_cache_groups[$group_id];
1307			}
1308			else {
1309				$group = $this->group($group_id)->result();
1310				$group_name = $group[0]->name;
1311				$this->_cache_groups[$group_id] = $group_name;
1312			}
1313			$this->_cache_user_in_group[$user_id][$group_id] = $group_name;
1314		}
1315		return $return;
1316	}
1317
1318	/**
1319	 * remove_from_group
1320	 *
1321	 * @return bool
1322	 * @author Ben Edmunds
1323	 **/
1324	public function remove_from_group($group_ids=false, $user_id=false)
1325	{
1326		$this->trigger_events('remove_from_group');
1327
1328		// user id is required
1329		if(empty($user_id))
1330		{
1331			return FALSE;
1332		}
1333
1334		// if group id(s) are passed remove user from the group(s)
1335		if( ! empty($group_ids))
1336		{
1337			if(!is_array($group_ids))
1338			{
1339				$group_ids = array($group_ids);
1340			}
1341
1342			foreach($group_ids as $group_id)
1343			{
1344				$this->db->delete($this->tables['users_groups'], array($this->join['groups'] => (int)$group_id, $this->join['users'] => (int)$user_id));
1345				if (isset($this->_cache_user_in_group[$user_id]) && isset($this->_cache_user_in_group[$user_id][$group_id]))
1346				{
1347					unset($this->_cache_user_in_group[$user_id][$group_id]);
1348				}
1349			}
1350
1351			$return = TRUE;
1352		}
1353		// otherwise remove user from all groups
1354		else
1355		{
1356			if ($return = $this->db->delete($this->tables['users_groups'], array($this->join['users'] => (int)$user_id))) {
1357				$this->_cache_user_in_group[$user_id] = array();
1358			}
1359		}
1360		return $return;
1361	}
1362
1363	/**
1364	 * groups
1365	 *
1366	 * @return object
1367	 * @author Ben Edmunds
1368	 **/
1369	public function groups()
1370	{
1371		$this->trigger_events('groups');
1372
1373		//run each where that was passed
1374		if (isset($this->_ion_where))
1375		{
1376			foreach ($this->_ion_where as $where)
1377			{
1378				$this->db->where($where);
1379			}
1380			$this->_ion_where = array();
1381		}
1382
1383		if (isset($this->_ion_limit) && isset($this->_ion_offset))
1384		{
1385			$this->db->limit($this->_ion_limit, $this->_ion_offset);
1386
1387			$this->_ion_limit  = NULL;
1388			$this->_ion_offset = NULL;
1389		}
1390		else if (isset($this->_ion_limit))
1391		{
1392			$this->db->limit($this->_ion_limit);
1393
1394			$this->_ion_limit  = NULL;
1395		}
1396
1397		//set the order
1398		if (isset($this->_ion_order_by) && isset($this->_ion_order))
1399		{
1400			$this->db->order_by($this->_ion_order_by, $this->_ion_order);
1401		}
1402
1403		$this->response = $this->db->get($this->tables['groups']);
1404
1405		return $this;
1406	}
1407
1408	/**
1409	 * group
1410	 *
1411	 * @return object
1412	 * @author Ben Edmunds
1413	 **/
1414	public function group($id = NULL)
1415	{
1416		$this->trigger_events('group');
1417
1418		if (isset($id))
1419		{
1420			$this->db->where($this->tables['groups'].'.id', $id);
1421		}
1422
1423		$this->limit(1);
1424
1425		return $this->groups();
1426	}
1427
1428	/**
1429	 * update
1430	 *
1431	 * @return bool
1432	 * @author Phil Sturgeon
1433	 **/
1434	public function update($id, array $data)
1435	{
1436		$this->trigger_events('pre_update_user');
1437
1438		$user = $this->user($id)->row();
1439
1440		$this->db->trans_begin();
1441
1442		if (array_key_exists($this->identity_column, $data) && $this->identity_check($data[$this->identity_column]) && $user->{$this->identity_column} !== $data[$this->identity_column])
1443		{
1444			$this->db->trans_rollback();
1445			$this->set_error('account_creation_duplicate_'.$this->identity_column);
1446
1447			$this->trigger_events(array('post_update_user', 'post_update_user_unsuccessful'));
1448			$this->set_error('update_unsuccessful');
1449
1450			return FALSE;
1451		}
1452
1453		// Filter the data passed
1454		$data = $this->_filter_data($this->tables['users'], $data);
1455
1456		if (array_key_exists('username', $data) || array_key_exists('password', $data) || array_key_exists('email', $data))
1457		{
1458			if (array_key_exists('password', $data))
1459			{
1460				if( ! empty($data['password']))
1461				{
1462					$data['password'] = $this->hash_password($data['password'], $user->salt);
1463				}
1464				else
1465				{
1466					// unset password so it doesn't effect database entry if no password passed
1467					unset($data['password']);
1468				}
1469			}
1470		}
1471
1472		$this->trigger_events('extra_where');
1473		$this->db->update($this->tables['users'], $data, array('id' => $user->id));
1474
1475		if ($this->db->trans_status() === FALSE)
1476		{
1477			$this->db->trans_rollback();
1478
1479			$this->trigger_events(array('post_update_user', 'post_update_user_unsuccessful'));
1480			$this->set_error('update_unsuccessful');
1481			return FALSE;
1482		}
1483
1484		$this->db->trans_commit();
1485
1486		$this->trigger_events(array('post_update_user', 'post_update_user_successful'));
1487		$this->set_message('update_successful');
1488		return TRUE;
1489	}
1490
1491	/**
1492	* delete_user
1493	*
1494	* @return bool
1495	* @author Phil Sturgeon
1496	**/
1497	public function delete_user($id)
1498	{
1499		$this->trigger_events('pre_delete_user');
1500
1501		$this->db->trans_begin();
1502
1503		// remove user from groups
1504		$this->remove_from_group(NULL, $id);
1505
1506		// delete user from users table
1507		$this->db->delete($this->tables['users'], array('id' => $id));
1508
1509		if ($this->db->trans_status() === FALSE)
1510		{
1511			$this->db->trans_rollback();
1512			$this->trigger_events(array('post_delete_user', 'post_delete_user_unsuccessful'));
1513			$this->set_error('delete_unsuccessful');
1514			return FALSE;
1515		}
1516
1517		$this->db->trans_commit();
1518
1519		$this->trigger_events(array('post_delete_user', 'post_delete_user_successful'));
1520		$this->set_message('delete_successful');
1521		return TRUE;
1522	}
1523
1524	/**
1525	 * update_last_login
1526	 *
1527	 * @return bool
1528	 * @author Ben Edmunds
1529	 **/
1530	public function update_last_login($id)
1531	{
1532		$this->trigger_events('update_last_login');
1533
1534		$this->load->helper('date');
1535
1536		$this->trigger_events('extra_where');
1537
1538		$this->db->update($this->tables['users'], array('last_login' => time()), array('id' => $id));
1539
1540		return $this->db->affected_rows() == 1;
1541	}
1542
1543	/**
1544	 * set_lang
1545	 *
1546	 * @return bool
1547	 * @author Ben Edmunds
1548	 **/
1549	public function set_lang($lang = 'en')
1550	{
1551		$this->trigger_events('set_lang');
1552
1553		// if the user_expire is set to zero we'll set the expiration two years from now.
1554		if($this->config->item('user_expire', 'ion_auth') === 0)
1555		{
1556			$expire = (60*60*24*365*2);
1557		}
1558		// otherwise use what is set
1559		else
1560		{
1561			$expire = $this->config->item('user_expire', 'ion_auth');
1562		}
1563
1564		set_cookie(array(
1565			'name'   => 'lang_code',
1566			'value'  => $lang,
1567			'expire' => $expire
1568		));
1569
1570		return TRUE;
1571	}
1572
1573	/**
1574	 * remember_user
1575	 *
1576	 * @return bool
1577	 * @author Ben Edmunds
1578	 **/
1579	public function remember_user($id)
1580	{
1581		$this->trigger_events('pre_remember_user');
1582
1583		if (!$id)
1584		{
1585			return FALSE;
1586		}
1587
1588		$user = $this->user($id)->row();
1589
1590		$salt = sha1($user->password);
1591
1592		$this->db->update($this->tables['users'], array('remember_code' => $salt), array('id' => $id));
1593
1594		if ($this->db->affected_rows() > -1)
1595		{
1596			// if the user_expire is set to zero we'll set the expiration two years from now.
1597			if($this->config->item('user_expire', 'ion_auth') === 0)
1598			{
1599				$expire = (60*60*24*365*2);
1600			}
1601			// otherwise use what is set
1602			else
1603			{
1604				$expire = $this->config->item('user_expire', 'ion_auth');
1605			}
1606
1607			set_cookie(array(
1608			    'name'   => 'identity',
1609			    'value'  => $user->{$this->identity_column},
1610			    'expire' => $expire
1611			));
1612
1613			set_cookie(array(
1614			    'name'   => 'remember_code',
1615			    'value'  => $salt,
1616			    'expire' => $expire
1617			));
1618
1619			$this->trigger_events(array('post_remember_user', 'remember_user_successful'));
1620			return TRUE;
1621		}
1622
1623		$this->trigger_events(array('post_remember_user', 'remember_user_unsuccessful'));
1624		return FALSE;
1625	}
1626
1627	/**
1628	 * login_remembed_user
1629	 *
1630	 * @return bool
1631	 * @author Ben Edmunds
1632	 **/
1633	public function login_remembered_user()
1634	{
1635		$this->trigger_events('pre_login_remembered_user');
1636
1637		//check for valid data
1638		if (!get_cookie('identity') || !get_cookie('remember_code') || !$this->identity_check(get_cookie('identity')))
1639		{
1640			$this->trigger_events(array('post_login_remembered_user', 'post_login_remembered_user_unsuccessful'));
1641			return FALSE;
1642		}
1643
1644		//get the user
1645		$this->trigger_events('extra_where');
1646		$query = $this->db->select($this->identity_column.', id')
1647		                  ->where($this->identity_column, get_cookie('identity'))
1648		                  ->where('remember_code', get_cookie('remember_code'))
1649		                  ->limit(1)
1650		                  ->get($this->tables['users']);
1651
1652		//if the user was found, sign them in
1653		if ($query->num_rows() == 1)
1654		{
1655			$user = $query->row();
1656
1657			$this->update_last_login($user->id);
1658
1659			$session_data = array(
1660			    $this->identity_column => $user->{$this->identity_column},
1661			    'id'                   => $user->id, //kept for backwards compatibility
1662			    'user_id'              => $user->id, //everyone likes to overwrite id so we'll use user_id
1663			);
1664
1665			$this->session->set_userdata($session_data);
1666
1667
1668			//extend the users cookies if the option is enabled
1669			if ($this->config->item('user_extend_on_login', 'ion_auth'))
1670			{
1671				$this->remember_user($user->id);
1672			}
1673
1674			$this->trigger_events(array('post_login_remembered_user', 'post_login_remembered_user_successful'));
1675			return TRUE;
1676		}
1677
1678		$this->trigger_events(array('post_login_remembered_user', 'post_login_remembered_user_unsuccessful'));
1679		return FALSE;
1680	}
1681
1682
1683	/**
1684	 * create_group
1685	 *
1686	 * @author aditya menon
1687	*/
1688	public function create_group($group_name = FALSE, $group_description = NULL)
1689	{
1690		// bail if the group name was not passed
1691		if(!$group_name)
1692		{
1693			return FALSE;
1694		}
1695
1696		// bail if the group name already exists
1697		$existing_group = $this->db->get_where('groups', array('name' => $group_name))->row();
1698		if(!is_null($existing_group->id))
1699		{
1700			$this->set_error('group_already_exists');
1701			return FALSE;
1702		}
1703
1704		// insert the new group
1705		$this->db->insert($this->tables['groups'], array('name' => $group_name, 'description' => $group_description));
1706		$group_id = $this->db->insert_id();
1707
1708		// report success
1709		$this->set_message('group_creation_successful');
1710		// return the brand new group id
1711		return $group_id;
1712	}
1713
1714	/**
1715	 * update_group
1716	 *
1717	 * @return bool
1718	 * @author aditya menon
1719	 **/
1720	public function update_group($group_id = FALSE, $group_name = FALSE, $group_description = NULL)
1721	{
1722		$mandatory = array($group_id, $group_name);
1723
1724		// bail if no group id or name given
1725		foreach ($mandatory as $mandatory_param) {		
1726			if(!$mandatory_param || empty($mandatory_param))
1727			{
1728				return FALSE;
1729			}
1730		}
1731
1732		// bail if the group name already exists
1733		$existing_group = $this->db->get_where('groups', array('name' => $group_name))->row();
1734		if(isset($existing_group->id) && $existing_group->id != $group_id)
1735		{
1736			$this->set_error('group_already_exists');
1737			return FALSE;
1738		}
1739
1740		$query_data = array(
1741			'name' => $group_name,
1742			'description' => $group_description,
1743		);
1744
1745		$this->db->update($this->tables['groups'], $query_data, array('id' => $group_id));
1746
1747		$this->set_message('group_update_successful');
1748
1749		return TRUE;
1750	}
1751
1752	/**
1753	* delete_group
1754	*
1755	* @return bool
1756	* @author aditya menon
1757	**/
1758	public function delete_group($group_id = FALSE)
1759	{
1760		// bail if mandatory param not set
1761		if(!$group_id || empty($group_id))
1762		{
1763			return FALSE;
1764		}
1765
1766		$this->trigger_events('pre_delete_group');
1767
1768		$this->db->trans_begin();
1769
1770		// remove all users from this group
1771		$this->db->delete($this->tables['users_groups'], array('group_id' => $group_id));
1772		// remove the group itself
1773		$this->db->delete($this->tables['groups'], array('id' => $group_id));
1774
1775		if ($this->db->trans_status() === FALSE)
1776		{
1777			$this->db->trans_rollback();
1778			$this->trigger_events(array('post_delete_group', 'post_delete_group_unsuccessful'));
1779			$this->set_error('group_delete_unsuccessful');
1780			return FALSE;
1781		}
1782
1783		$this->db->trans_commit();
1784
1785		$this->trigger_events(array('post_delete_group', 'post_delete_group_successful'));
1786		$this->set_message('group_delete_successful');
1787		return TRUE;
1788	}
1789
1790	public function set_hook($event, $name, $class, $method, $arguments)
1791	{
1792		$this->_ion_hooks->{$event}[$name] = new stdClass;
1793		$this->_ion_hooks->{$event}[$name]->class     = $class;
1794		$this->_ion_hooks->{$event}[$name]->method    = $method;
1795		$this->_ion_hooks->{$event}[$name]->arguments = $arguments;
1796	}
1797
1798	public function remove_hook($event, $name)
1799	{
1800		if (isset($this->_ion_hooks->{$event}[$name]))
1801		{
1802			unset($this->_ion_hooks->{$event}[$name]);
1803		}
1804	}
1805
1806	public function remove_hooks($event)
1807	{
1808		if (isset($this->_ion_hooks->$event))
1809		{
1810			unset($this->_ion_hooks->$event);
1811		}
1812	}
1813
1814	protected function _call_hook($event, $name)
1815	{
1816		if (isset($this->_ion_hooks->{$event}[$name]) && method_exists($this->_ion_hooks->{$event}[$name]->class, $this->_ion_hooks->{$event}[$name]->method))
1817		{
1818			$hook = $this->_ion_hooks->{$event}[$name];
1819
1820			return call_user_func_array(array($hook->class, $hook->method), $hook->arguments);
1821		}
1822
1823		return FALSE;
1824	}
1825
1826	public function trigger_events($events)
1827	{
1828		if (is_array($events) && !empty($events))
1829		{
1830			foreach ($events as $event)
1831			{
1832				$this->trigger_events($event);
1833			}
1834		}
1835		else
1836		{
1837			if (isset($this->_ion_hooks->$events) && !empty($this->_ion_hooks->$events))
1838			{
1839				foreach ($this->_ion_hooks->$events as $name => $hook)
1840				{
1841					$this->_call_hook($events, $name);
1842				}
1843			}
1844		}
1845	}
1846
1847	/**
1848	 * set_message_delimiters
1849	 *
1850	 * Set the message delimiters
1851	 *
1852	 * @return void
1853	 * @author Ben Edmunds
1854	 **/
1855	public function set_message_delimiters($start_delimiter, $end_delimiter)
1856	{
1857		$this->message_start_delimiter = $start_delimiter;
1858		$this->message_end_delimiter   = $end_delimiter;
1859
1860		return TRUE;
1861	}
1862
1863	/**
1864	 * set_error_delimiters
1865	 *
1866	 * Set the error delimiters
1867	 *
1868	 * @return void
1869	 * @author Ben Edmunds
1870	 **/
1871	public function set_error_delimiters($start_delimiter, $end_delimiter)
1872	{
1873		$this->error_start_delimiter = $start_delimiter;
1874		$this->error_end_delimiter   = $end_delimiter;
1875
1876		return TRUE;
1877	}
1878
1879	/**
1880	 * set_message
1881	 *
1882	 * Set a message
1883	 *
1884	 * @return void
1885	 * @author Ben Edmunds
1886	 **/
1887	public function set_message($message)
1888	{
1889		$this->messages[] = $message;
1890
1891		return $message;
1892	}
1893
1894	/**
1895	 * messages
1896	 *
1897	 * Get the messages
1898	 *
1899	 * @return void
1900	 * @author Ben Edmunds
1901	 **/
1902	public function messages()
1903	{
1904		$_output = '';
1905		foreach ($this->messages as $message)
1906		{
1907			$messageLang = $this->lang->line($message) ? $this->lang->line($message) : '##' . $message . '##';
1908			$_output .= $this->message_start_delimiter . $messageLang . $this->message_end_delimiter;
1909		}
1910
1911		return $_output;
1912	}
1913
1914	/**
1915	 * messages as array
1916	 *
1917	 * Get the messages as an array
1918	 *
1919	 * @return array
1920	 * @author Raul Baldner Junior
1921	 **/
1922	public function messages_array($langify = TRUE)
1923	{
1924		if ($langify)
1925		{
1926			$_output = array();
1927			foreach ($this->messages as $message)
1928			{
1929				$messageLang = $this->lang->line($message) ? $this->lang->line($message) : '##' . $message . '##';
1930				$_output[] = $this->message_start_delimiter . $messageLang . $this->message_end_delimiter;
1931			}
1932			return $_output;
1933		}
1934		else
1935		{
1936			return $this->messages;
1937		}
1938	}
1939
1940	/**
1941	 * set_error
1942	 *
1943	 * Set an error message
1944	 *
1945	 * @return void
1946	 * @author Ben Edmunds
1947	 **/
1948	public function set_error($error)
1949	{
1950		$this->errors[] = $error;
1951
1952		return $error;
1953	}
1954
1955	/**
1956	 * errors
1957	 *
1958	 * Get the error message
1959	 *
1960	 * @return void
1961	 * @author Ben Edmunds
1962	 **/
1963	public function errors()
1964	{
1965		$_output = '';
1966		foreach ($this->errors as $error)
1967		{
1968			$errorLang = $this->lang->line($error) ? $this->lang->line($error) : '##' . $error . '##';
1969			$_output .= $this->error_start_delimiter . $errorLang . $this->error_end_delimiter;
1970		}
1971
1972		return $_output;
1973	}
1974
1975	/**
1976	 * errors as array
1977	 *
1978	 * Get the error messages as an array
1979	 *
1980	 * @return array
1981	 * @author Raul Baldner Junior
1982	 **/
1983	public function errors_array($langify = TRUE)
1984	{
1985		if ($langify)
1986		{
1987			$_output = array();
1988			foreach ($this->errors as $error)
1989			{
1990				$errorLang = $this->lang->line($error) ? $this->lang->line($error) : '##' . $error . '##';
1991				$_output[] = $this->error_start_delimiter . $errorLang . $this->error_end_delimiter;
1992			}
1993			return $_output;
1994		}
1995		else
1996		{
1997			return $this->errors;
1998		}
1999	}
2000
2001	protected function _filter_data($table, $data)
2002	{
2003		$filtered_data = array();
2004		$columns = $this->db->list_fields($table);
2005
2006		if (is_array($data))
2007		{
2008			foreach ($columns as $column)
2009			{
2010				if (array_key_exists($column, $data))
2011					$filtered_data[$column] = $data[$column];
2012			}
2013		}
2014
2015		return $filtered_data;
2016	}
2017
2018	protected function _prepare_ip($ip_address) {
2019		if ($this->db->platform() === 'postgre' || $this->db->platform() === 'sqlsrv' || $this->db->platform() === 'mssql')
2020		{
2021			return $ip_address;
2022		}
2023		else
2024		{
2025			return inet_pton($ip_address);
2026		}
2027	}
2028}