PageRenderTime 108ms CodeModel.GetById 41ms app.highlight 24ms RepoModel.GetById 36ms app.codeStats 0ms

/system/libraries/Session.php

https://bitbucket.org/hlevine/myclientbase-south-african-version
PHP | 777 lines | 414 code | 113 blank | 250 comment | 61 complexity | 0f165598001e26cc9667dadd78018fe6 MD5 | raw file
  1<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2/**
  3 * CodeIgniter
  4 *
  5 * An open source application development framework for PHP 5.1.6 or newer
  6 *
  7 * @package		CodeIgniter
  8 * @author		ExpressionEngine Dev Team
  9 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
 10 * @license		http://codeigniter.com/user_guide/license.html
 11 * @link		http://codeigniter.com
 12 * @since		Version 1.0
 13 * @filesource
 14 */
 15
 16// ------------------------------------------------------------------------
 17
 18/**
 19 * Session Class
 20 *
 21 * @package		CodeIgniter
 22 * @subpackage	Libraries
 23 * @category	Sessions
 24 * @author		ExpressionEngine Dev Team
 25 * @link		http://codeigniter.com/user_guide/libraries/sessions.html
 26 */
 27class CI_Session {
 28
 29	var $sess_encrypt_cookie		= FALSE;
 30	var $sess_use_database			= FALSE;
 31	var $sess_table_name			= '';
 32	var $sess_expiration			= 7200;
 33	var $sess_expire_on_close		= FALSE;
 34	var $sess_match_ip				= FALSE;
 35	var $sess_match_useragent		= TRUE;
 36	var $sess_cookie_name			= 'ci_session';
 37	var $cookie_prefix				= '';
 38	var $cookie_path				= '';
 39	var $cookie_domain				= '';
 40	var $cookie_secure				= FALSE;
 41	var $sess_time_to_update		= 300;
 42	var $encryption_key				= '';
 43	var $flashdata_key				= 'flash';
 44	var $time_reference				= 'time';
 45	var $gc_probability				= 5;
 46	var $userdata					= array();
 47	var $CI;
 48	var $now;
 49
 50	/**
 51	 * Session Constructor
 52	 *
 53	 * The constructor runs the session routines automatically
 54	 * whenever the class is instantiated.
 55	 */
 56	public function __construct($params = array())
 57	{
 58		log_message('debug', "Session Class Initialized");
 59
 60		// Set the super object to a local variable for use throughout the class
 61		$this->CI =& get_instance();
 62
 63		// Set all the session preferences, which can either be set
 64		// manually via the $params array above or via the config file
 65		foreach (array('sess_encrypt_cookie', 'sess_use_database', 'sess_table_name', 'sess_expiration', 'sess_expire_on_close', 'sess_match_ip', 'sess_match_useragent', 'sess_cookie_name', 'cookie_path', 'cookie_domain', 'cookie_secure', 'sess_time_to_update', 'time_reference', 'cookie_prefix', 'encryption_key') as $key)
 66		{
 67			$this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key);
 68		}
 69
 70		if ($this->encryption_key == '')
 71		{
 72			show_error('In order to use the Session class you are required to set an encryption key in your config file.');
 73		}
 74
 75		// Load the string helper so we can use the strip_slashes() function
 76		$this->CI->load->helper('string');
 77
 78		// Do we need encryption? If so, load the encryption class
 79		if ($this->sess_encrypt_cookie == TRUE)
 80		{
 81			$this->CI->load->library('encrypt');
 82		}
 83
 84		// Are we using a database?  If so, load it
 85		if ($this->sess_use_database === TRUE AND $this->sess_table_name != '')
 86		{
 87			$this->CI->load->database();
 88		}
 89
 90		// Set the "now" time.  Can either be GMT or server time, based on the
 91		// config prefs.  We use this to set the "last activity" time
 92		$this->now = $this->_get_time();
 93
 94		// Set the session length. If the session expiration is
 95		// set to zero we'll set the expiration two years from now.
 96		if ($this->sess_expiration == 0)
 97		{
 98			$this->sess_expiration = (60*60*24*365*2);
 99		}
100		
101		// Set the cookie name
102		$this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;
103
104		// Run the Session routine. If a session doesn't exist we'll
105		// create a new one.  If it does, we'll update it.
106		if ( ! $this->sess_read())
107		{
108			$this->sess_create();
109		}
110		else
111		{
112			$this->sess_update();
113		}
114
115		// Delete 'old' flashdata (from last request)
116		$this->_flashdata_sweep();
117
118		// Mark all new flashdata as old (data will be deleted before next request)
119		$this->_flashdata_mark();
120
121		// Delete expired sessions if necessary
122		$this->_sess_gc();
123
124		log_message('debug', "Session routines successfully run");
125	}
126
127	// --------------------------------------------------------------------
128
129	/**
130	 * Fetch the current session data if it exists
131	 *
132	 * @access	public
133	 * @return	bool
134	 */
135	function sess_read()
136	{
137		// Fetch the cookie
138		$session = $this->CI->input->cookie($this->sess_cookie_name);
139
140		// No cookie?  Goodbye cruel world!...
141		if ($session === FALSE)
142		{
143			log_message('debug', 'A session cookie was not found.');
144			return FALSE;
145		}
146
147		// Decrypt the cookie data
148		if ($this->sess_encrypt_cookie == TRUE)
149		{
150			$session = $this->CI->encrypt->decode($session);
151		}
152		else
153		{
154			// encryption was not used, so we need to check the md5 hash
155			$hash	 = substr($session, strlen($session)-32); // get last 32 chars
156			$session = substr($session, 0, strlen($session)-32);
157
158			// Does the md5 hash match?  This is to prevent manipulation of session data in userspace
159			if ($hash !==  md5($session.$this->encryption_key))
160			{
161				log_message('error', 'The session cookie data did not match what was expected. This could be a possible hacking attempt.');
162				$this->sess_destroy();
163				return FALSE;
164			}
165		}
166
167		// Unserialize the session array
168		$session = $this->_unserialize($session);
169
170		// Is the session data we unserialized an array with the correct format?
171		if ( ! is_array($session) OR ! isset($session['session_id']) OR ! isset($session['ip_address']) OR ! isset($session['user_agent']) OR ! isset($session['last_activity']))
172		{
173			$this->sess_destroy();
174			return FALSE;
175		}
176
177		// Is the session current?
178		if (($session['last_activity'] + $this->sess_expiration) < $this->now)
179		{
180			$this->sess_destroy();
181			return FALSE;
182		}
183
184		// Does the IP Match?
185		if ($this->sess_match_ip == TRUE AND $session['ip_address'] != $this->CI->input->ip_address())
186		{
187			$this->sess_destroy();
188			return FALSE;
189		}
190
191		// Does the User Agent Match?
192		if ($this->sess_match_useragent == TRUE AND trim($session['user_agent']) != trim(substr($this->CI->input->user_agent(), 0, 120)))
193		{
194			$this->sess_destroy();
195			return FALSE;
196		}
197
198		// Is there a corresponding session in the DB?
199		if ($this->sess_use_database === TRUE)
200		{
201			$this->CI->db->where('session_id', $session['session_id']);
202
203			if ($this->sess_match_ip == TRUE)
204			{
205				$this->CI->db->where('ip_address', $session['ip_address']);
206			}
207
208			if ($this->sess_match_useragent == TRUE)
209			{
210				$this->CI->db->where('user_agent', $session['user_agent']);
211			}
212
213			$query = $this->CI->db->get($this->sess_table_name);
214
215			// No result?  Kill it!
216			if ($query->num_rows() == 0)
217			{
218				$this->sess_destroy();
219				return FALSE;
220			}
221
222			// Is there custom data?  If so, add it to the main session array
223			$row = $query->row();
224			if (isset($row->user_data) AND $row->user_data != '')
225			{
226				$custom_data = $this->_unserialize($row->user_data);
227
228				if (is_array($custom_data))
229				{
230					foreach ($custom_data as $key => $val)
231					{
232						$session[$key] = $val;
233					}
234				}
235			}
236		}
237
238		// Session is valid!
239		$this->userdata = $session;
240		unset($session);
241
242		return TRUE;
243	}
244
245	// --------------------------------------------------------------------
246
247	/**
248	 * Write the session data
249	 *
250	 * @access	public
251	 * @return	void
252	 */
253	function sess_write()
254	{
255		// Are we saving custom data to the DB?  If not, all we do is update the cookie
256		if ($this->sess_use_database === FALSE)
257		{
258			$this->_set_cookie();
259			return;
260		}
261
262		// set the custom userdata, the session data we will set in a second
263		$custom_userdata = $this->userdata;
264		$cookie_userdata = array();
265
266		// Before continuing, we need to determine if there is any custom data to deal with.
267		// Let's determine this by removing the default indexes to see if there's anything left in the array
268		// and set the session data while we're at it
269		foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
270		{
271			unset($custom_userdata[$val]);
272			$cookie_userdata[$val] = $this->userdata[$val];
273		}
274
275		// Did we find any custom data?  If not, we turn the empty array into a string
276		// since there's no reason to serialize and store an empty array in the DB
277		if (count($custom_userdata) === 0)
278		{
279			$custom_userdata = '';
280		}
281		else
282		{
283			// Serialize the custom data array so we can store it
284			$custom_userdata = $this->_serialize($custom_userdata);
285		}
286
287		// Run the update query
288		$this->CI->db->where('session_id', $this->userdata['session_id']);
289		$this->CI->db->update($this->sess_table_name, array('last_activity' => $this->userdata['last_activity'], 'user_data' => $custom_userdata));
290
291		// Write the cookie.  Notice that we manually pass the cookie data array to the
292		// _set_cookie() function. Normally that function will store $this->userdata, but
293		// in this case that array contains custom data, which we do not want in the cookie.
294		$this->_set_cookie($cookie_userdata);
295	}
296
297	// --------------------------------------------------------------------
298
299	/**
300	 * Create a new session
301	 *
302	 * @access	public
303	 * @return	void
304	 */
305	function sess_create()
306	{
307		$sessid = '';
308		while (strlen($sessid) < 32)
309		{
310			$sessid .= mt_rand(0, mt_getrandmax());
311		}
312
313		// To make the session ID even more secure we'll combine it with the user's IP
314		$sessid .= $this->CI->input->ip_address();
315
316		$this->userdata = array(
317							'session_id'	=> md5(uniqid($sessid, TRUE)),
318							'ip_address'	=> $this->CI->input->ip_address(),
319							'user_agent'	=> substr($this->CI->input->user_agent(), 0, 120),
320							'last_activity'	=> $this->now,
321							'user_data'		=> ''
322							);
323
324
325		// Save the data to the DB if needed
326		if ($this->sess_use_database === TRUE)
327		{
328			$this->CI->db->query($this->CI->db->insert_string($this->sess_table_name, $this->userdata));
329		}
330
331		// Write the cookie
332		$this->_set_cookie();
333	}
334
335	// --------------------------------------------------------------------
336
337	/**
338	 * Update an existing session
339	 *
340	 * @access	public
341	 * @return	void
342	 */
343	function sess_update()
344	{
345		// We only update the session every five minutes by default
346		if (($this->userdata['last_activity'] + $this->sess_time_to_update) >= $this->now)
347		{
348			return;
349		}
350
351		// Save the old session id so we know which record to
352		// update in the database if we need it
353		$old_sessid = $this->userdata['session_id'];
354		$new_sessid = '';
355		while (strlen($new_sessid) < 32)
356		{
357			$new_sessid .= mt_rand(0, mt_getrandmax());
358		}
359
360		// To make the session ID even more secure we'll combine it with the user's IP
361		$new_sessid .= $this->CI->input->ip_address();
362
363		// Turn it into a hash
364		$new_sessid = md5(uniqid($new_sessid, TRUE));
365
366		// Update the session data in the session data array
367		$this->userdata['session_id'] = $new_sessid;
368		$this->userdata['last_activity'] = $this->now;
369
370		// _set_cookie() will handle this for us if we aren't using database sessions
371		// by pushing all userdata to the cookie.
372		$cookie_data = NULL;
373
374		// Update the session ID and last_activity field in the DB if needed
375		if ($this->sess_use_database === TRUE)
376		{
377			// set cookie explicitly to only have our session data
378			$cookie_data = array();
379			foreach (array('session_id','ip_address','user_agent','last_activity') as $val)
380			{
381				$cookie_data[$val] = $this->userdata[$val];
382			}
383
384			$this->CI->db->query($this->CI->db->update_string($this->sess_table_name, array('last_activity' => $this->now, 'session_id' => $new_sessid), array('session_id' => $old_sessid)));
385		}
386
387		// Write the cookie
388		$this->_set_cookie($cookie_data);
389	}
390
391	// --------------------------------------------------------------------
392
393	/**
394	 * Destroy the current session
395	 *
396	 * @access	public
397	 * @return	void
398	 */
399	function sess_destroy()
400	{
401		// Kill the session DB row
402		if ($this->sess_use_database === TRUE AND isset($this->userdata['session_id']))
403		{
404			$this->CI->db->where('session_id', $this->userdata['session_id']);
405			$this->CI->db->delete($this->sess_table_name);
406		}
407
408		// Kill the cookie
409		setcookie(
410					$this->sess_cookie_name,
411					addslashes(serialize(array())),
412					($this->now - 31500000),
413					$this->cookie_path,
414					$this->cookie_domain,
415					0
416				);
417	}
418
419	// --------------------------------------------------------------------
420
421	/**
422	 * Fetch a specific item from the session array
423	 *
424	 * @access	public
425	 * @param	string
426	 * @return	string
427	 */
428	function userdata($item)
429	{
430		return ( ! isset($this->userdata[$item])) ? FALSE : $this->userdata[$item];
431	}
432
433	// --------------------------------------------------------------------
434
435	/**
436	 * Fetch all session data
437	 *
438	 * @access	public
439	 * @return	array
440	 */
441	function all_userdata()
442	{
443		return $this->userdata;
444	}
445
446	// --------------------------------------------------------------------
447
448	/**
449	 * Add or change data in the "userdata" array
450	 *
451	 * @access	public
452	 * @param	mixed
453	 * @param	string
454	 * @return	void
455	 */
456	function set_userdata($newdata = array(), $newval = '')
457	{
458		if (is_string($newdata))
459		{
460			$newdata = array($newdata => $newval);
461		}
462
463		if (count($newdata) > 0)
464		{
465			foreach ($newdata as $key => $val)
466			{
467				$this->userdata[$key] = $val;
468			}
469		}
470
471		$this->sess_write();
472	}
473
474	// --------------------------------------------------------------------
475
476	/**
477	 * Delete a session variable from the "userdata" array
478	 *
479	 * @access	array
480	 * @return	void
481	 */
482	function unset_userdata($newdata = array())
483	{
484		if (is_string($newdata))
485		{
486			$newdata = array($newdata => '');
487		}
488
489		if (count($newdata) > 0)
490		{
491			foreach ($newdata as $key => $val)
492			{
493				unset($this->userdata[$key]);
494			}
495		}
496
497		$this->sess_write();
498	}
499
500	// ------------------------------------------------------------------------
501
502	/**
503	 * Add or change flashdata, only available
504	 * until the next request
505	 *
506	 * @access	public
507	 * @param	mixed
508	 * @param	string
509	 * @return	void
510	 */
511	function set_flashdata($newdata = array(), $newval = '')
512	{
513		if (is_string($newdata))
514		{
515			$newdata = array($newdata => $newval);
516		}
517
518		if (count($newdata) > 0)
519		{
520			foreach ($newdata as $key => $val)
521			{
522				$flashdata_key = $this->flashdata_key.':new:'.$key;
523				$this->set_userdata($flashdata_key, $val);
524			}
525		}
526	}
527
528	// ------------------------------------------------------------------------
529
530	/**
531	 * Keeps existing flashdata available to next request.
532	 *
533	 * @access	public
534	 * @param	string
535	 * @return	void
536	 */
537	function keep_flashdata($key)
538	{
539		// 'old' flashdata gets removed.  Here we mark all
540		// flashdata as 'new' to preserve it from _flashdata_sweep()
541		// Note the function will return FALSE if the $key
542		// provided cannot be found
543		$old_flashdata_key = $this->flashdata_key.':old:'.$key;
544		$value = $this->userdata($old_flashdata_key);
545
546		$new_flashdata_key = $this->flashdata_key.':new:'.$key;
547		$this->set_userdata($new_flashdata_key, $value);
548	}
549
550	// ------------------------------------------------------------------------
551
552	/**
553	 * Fetch a specific flashdata item from the session array
554	 *
555	 * @access	public
556	 * @param	string
557	 * @return	string
558	 */
559	function flashdata($key)
560	{
561		$flashdata_key = $this->flashdata_key.':old:'.$key;
562		return $this->userdata($flashdata_key);
563	}
564
565	// ------------------------------------------------------------------------
566
567	/**
568	 * Identifies flashdata as 'old' for removal
569	 * when _flashdata_sweep() runs.
570	 *
571	 * @access	private
572	 * @return	void
573	 */
574	function _flashdata_mark()
575	{
576		$userdata = $this->all_userdata();
577		foreach ($userdata as $name => $value)
578		{
579			$parts = explode(':new:', $name);
580			if (is_array($parts) && count($parts) === 2)
581			{
582				$new_name = $this->flashdata_key.':old:'.$parts[1];
583				$this->set_userdata($new_name, $value);
584				$this->unset_userdata($name);
585			}
586		}
587	}
588
589	// ------------------------------------------------------------------------
590
591	/**
592	 * Removes all flashdata marked as 'old'
593	 *
594	 * @access	private
595	 * @return	void
596	 */
597
598	function _flashdata_sweep()
599	{
600		$userdata = $this->all_userdata();
601		foreach ($userdata as $key => $value)
602		{
603			if (strpos($key, ':old:'))
604			{
605				$this->unset_userdata($key);
606			}
607		}
608
609	}
610
611	// --------------------------------------------------------------------
612
613	/**
614	 * Get the "now" time
615	 *
616	 * @access	private
617	 * @return	string
618	 */
619	function _get_time()
620	{
621		if (strtolower($this->time_reference) == 'gmt')
622		{
623			$now = time();
624			$time = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now));
625		}
626		else
627		{
628			$time = time();
629		}
630
631		return $time;
632	}
633
634	// --------------------------------------------------------------------
635
636	/**
637	 * Write the session cookie
638	 *
639	 * @access	public
640	 * @return	void
641	 */
642	function _set_cookie($cookie_data = NULL)
643	{
644		if (is_null($cookie_data))
645		{
646			$cookie_data = $this->userdata;
647		}
648
649		// Serialize the userdata for the cookie
650		$cookie_data = $this->_serialize($cookie_data);
651
652		if ($this->sess_encrypt_cookie == TRUE)
653		{
654			$cookie_data = $this->CI->encrypt->encode($cookie_data);
655		}
656		else
657		{
658			// if encryption is not used, we provide an md5 hash to prevent userside tampering
659			$cookie_data = $cookie_data.md5($cookie_data.$this->encryption_key);
660		}
661
662		$expire = ($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration + time();
663
664		// Set the cookie
665		setcookie(
666					$this->sess_cookie_name,
667					$cookie_data,
668					$expire,
669					$this->cookie_path,
670					$this->cookie_domain,
671					$this->cookie_secure
672				);
673	}
674
675	// --------------------------------------------------------------------
676
677	/**
678	 * Serialize an array
679	 *
680	 * This function first converts any slashes found in the array to a temporary
681	 * marker, so when it gets unserialized the slashes will be preserved
682	 *
683	 * @access	private
684	 * @param	array
685	 * @return	string
686	 */
687	function _serialize($data)
688	{
689		if (is_array($data))
690		{
691			foreach ($data as $key => $val)
692			{
693				if (is_string($val))
694				{
695					$data[$key] = str_replace('\\', '{{slash}}', $val);
696				}
697			}
698		}
699		else
700		{
701			if (is_string($data))
702			{
703				$data = str_replace('\\', '{{slash}}', $data);
704			}
705		}
706
707		return serialize($data);
708	}
709
710	// --------------------------------------------------------------------
711
712	/**
713	 * Unserialize
714	 *
715	 * This function unserializes a data string, then converts any
716	 * temporary slash markers back to actual slashes
717	 *
718	 * @access	private
719	 * @param	array
720	 * @return	string
721	 */
722	function _unserialize($data)
723	{
724		$data = @unserialize(strip_slashes($data));
725
726		if (is_array($data))
727		{
728			foreach ($data as $key => $val)
729			{
730				if (is_string($val))
731				{
732					$data[$key] = str_replace('{{slash}}', '\\', $val);
733				}
734			}
735
736			return $data;
737		}
738
739		return (is_string($data)) ? str_replace('{{slash}}', '\\', $data) : $data;
740	}
741
742	// --------------------------------------------------------------------
743
744	/**
745	 * Garbage collection
746	 *
747	 * This deletes expired session rows from database
748	 * if the probability percentage is met
749	 *
750	 * @access	public
751	 * @return	void
752	 */
753	function _sess_gc()
754	{
755		if ($this->sess_use_database != TRUE)
756		{
757			return;
758		}
759
760		srand(time());
761		if ((rand() % 100) < $this->gc_probability)
762		{
763			$expire = $this->now - $this->sess_expiration;
764
765			$this->CI->db->where("last_activity < {$expire}");
766			$this->CI->db->delete($this->sess_table_name);
767
768			log_message('debug', 'Session garbage collection performed.');
769		}
770	}
771
772
773}
774// END Session Class
775
776/* End of file Session.php */
777/* Location: ./system/libraries/Session.php */