/lenasys/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/lenasys/trunk
20.1.1 by galaxyAbstractor
* Added an simple admin panel to the codeviewer-cmssy stuff
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
 * Input Class
20
 *
21
 * Pre-processes global input data for security
22
 *
23
 * @package		CodeIgniter
24
 * @subpackage	Libraries
25
 * @category	Input
26
 * @author		ExpressionEngine Dev Team
27
 * @link		http://codeigniter.com/user_guide/libraries/input.html
28
 */
29
class CI_Input {
30
31
	/**
32
	 * IP address of the current user
33
	 *
34
	 * @var string
35
	 */
36
	var $ip_address				= FALSE;
37
	/**
38
	 * user agent (web browser) being used by the current user
39
	 *
40
	 * @var string
41
	 */
42
	var $user_agent				= FALSE;
43
	/**
44
	 * If FALSE, then $_GET will be set to an empty array
45
	 *
46
	 * @var bool
47
	 */
48
	var $_allow_get_array		= TRUE;
49
	/**
50
	 * If TRUE, then newlines are standardized
51
	 *
52
	 * @var bool
53
	 */
54
	var $_standardize_newlines	= TRUE;
55
	/**
56
	 * Determines whether the XSS filter is always active when GET, POST or COOKIE data is encountered
57
	 * Set automatically based on config setting
58
	 *
59
	 * @var bool
60
	 */
61
	var $_enable_xss			= FALSE;
62
	/**
63
	 * Enables a CSRF cookie token to be set.
64
	 * Set automatically based on config setting
65
	 *
66
	 * @var bool
67
	 */
68
	var $_enable_csrf			= FALSE;
69
	/**
70
	 * List of all HTTP request headers
71
	 *
72
	 * @var array
73
	 */
74
	protected $headers			= array();
75
76
	/**
77
	 * Constructor
78
	 *
79
	 * Sets whether to globally enable the XSS processing
80
	 * and whether to allow the $_GET array
81
	 *
82
	 * @return	void
83
	 */
84
	public function __construct()
85
	{
86
		log_message('debug', "Input Class Initialized");
87
88
		$this->_allow_get_array	= (config_item('allow_get_array') === TRUE);
89
		$this->_enable_xss		= (config_item('global_xss_filtering') === TRUE);
90
		$this->_enable_csrf		= (config_item('csrf_protection') === TRUE);
91
92
		global $SEC;
93
		$this->security =& $SEC;
94
95
		// Do we need the UTF-8 class?
96
		if (UTF8_ENABLED === TRUE)
97
		{
98
			global $UNI;
99
			$this->uni =& $UNI;
100
		}
101
102
		// Sanitize global arrays
103
		$this->_sanitize_globals();
104
	}
105
106
	// --------------------------------------------------------------------
107
108
	/**
109
	 * Fetch from array
110
	 *
111
	 * This is a helper function to retrieve values from global arrays
112
	 *
113
	 * @access	private
114
	 * @param	array
115
	 * @param	string
116
	 * @param	bool
117
	 * @return	string
118
	 */
119
	function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE)
120
	{
121
		if ( ! isset($array[$index]))
122
		{
123
			return FALSE;
124
		}
125
126
		if ($xss_clean === TRUE)
127
		{
128
			return $this->security->xss_clean($array[$index]);
129
		}
130
131
		return $array[$index];
132
	}
133
134
	// --------------------------------------------------------------------
135
136
	/**
137
	* Fetch an item from the GET array
138
	*
139
	* @access	public
140
	* @param	string
141
	* @param	bool
142
	* @return	string
143
	*/
144
	function get($index = NULL, $xss_clean = FALSE)
145
	{
146
		// Check if a field has been provided
147
		if ($index === NULL AND ! empty($_GET))
148
		{
149
			$get = array();
150
151
			// loop through the full _GET array
152
			foreach (array_keys($_GET) as $key)
153
			{
154
				$get[$key] = $this->_fetch_from_array($_GET, $key, $xss_clean);
155
			}
156
			return $get;
157
		}
158
159
		return $this->_fetch_from_array($_GET, $index, $xss_clean);
160
	}
161
162
	// --------------------------------------------------------------------
163
164
	/**
165
	* Fetch an item from the POST array
166
	*
167
	* @access	public
168
	* @param	string
169
	* @param	bool
170
	* @return	string
171
	*/
172
	function post($index = NULL, $xss_clean = FALSE)
173
	{
174
		// Check if a field has been provided
175
		if ($index === NULL AND ! empty($_POST))
176
		{
177
			$post = array();
178
179
			// Loop through the full _POST array and return it
180
			foreach (array_keys($_POST) as $key)
181
			{
182
				$post[$key] = $this->_fetch_from_array($_POST, $key, $xss_clean);
183
			}
184
			return $post;
185
		}
186
187
		return $this->_fetch_from_array($_POST, $index, $xss_clean);
188
	}
189
190
191
	// --------------------------------------------------------------------
192
193
	/**
194
	* Fetch an item from either the GET array or the POST
195
	*
196
	* @access	public
197
	* @param	string	The index key
198
	* @param	bool	XSS cleaning
199
	* @return	string
200
	*/
201
	function get_post($index = '', $xss_clean = FALSE)
202
	{
203
		if ( ! isset($_POST[$index]) )
204
		{
205
			return $this->get($index, $xss_clean);
206
		}
207
		else
208
		{
209
			return $this->post($index, $xss_clean);
210
		}
211
	}
212
213
	// --------------------------------------------------------------------
214
215
	/**
216
	* Fetch an item from the COOKIE array
217
	*
218
	* @access	public
219
	* @param	string
220
	* @param	bool
221
	* @return	string
222
	*/
223
	function cookie($index = '', $xss_clean = FALSE)
224
	{
225
		return $this->_fetch_from_array($_COOKIE, $index, $xss_clean);
226
	}
227
228
	// ------------------------------------------------------------------------
229
230
	/**
231
	* Set cookie
232
	*
233
	* Accepts six parameter, or you can submit an associative
234
	* array in the first parameter containing all the values.
235
	*
236
	* @access	public
237
	* @param	mixed
238
	* @param	string	the value of the cookie
239
	* @param	string	the number of seconds until expiration
240
	* @param	string	the cookie domain.  Usually:  .yourdomain.com
241
	* @param	string	the cookie path
242
	* @param	string	the cookie prefix
243
	* @param	bool	true makes the cookie secure
244
	* @return	void
245
	*/
246
	function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE)
247
	{
248
		if (is_array($name))
249
		{
250
			// always leave 'name' in last place, as the loop will break otherwise, due to $$item
251
			foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'name') as $item)
252
			{
253
				if (isset($name[$item]))
254
				{
255
					$$item = $name[$item];
256
				}
257
			}
258
		}
259
260
		if ($prefix == '' AND config_item('cookie_prefix') != '')
261
		{
262
			$prefix = config_item('cookie_prefix');
263
		}
264
		if ($domain == '' AND config_item('cookie_domain') != '')
265
		{
266
			$domain = config_item('cookie_domain');
267
		}
268
		if ($path == '/' AND config_item('cookie_path') != '/')
269
		{
270
			$path = config_item('cookie_path');
271
		}
272
		if ($secure == FALSE AND config_item('cookie_secure') != FALSE)
273
		{
274
			$secure = config_item('cookie_secure');
275
		}
276
277
		if ( ! is_numeric($expire))
278
		{
279
			$expire = time() - 86500;
280
		}
281
		else
282
		{
283
			$expire = ($expire > 0) ? time() + $expire : 0;
284
		}
285
286
		setcookie($prefix.$name, $value, $expire, $path, $domain, $secure);
287
	}
288
289
	// --------------------------------------------------------------------
290
291
	/**
292
	* Fetch an item from the SERVER array
293
	*
294
	* @access	public
295
	* @param	string
296
	* @param	bool
297
	* @return	string
298
	*/
299
	function server($index = '', $xss_clean = FALSE)
300
	{
301
		return $this->_fetch_from_array($_SERVER, $index, $xss_clean);
302
	}
303
304
	// --------------------------------------------------------------------
305
306
	/**
307
	* Fetch the IP Address
308
	*
309
	* @return	string
310
	*/
311
	public function ip_address()
312
	{
313
		if ($this->ip_address !== FALSE)
314
		{
315
			return $this->ip_address;
316
		}
317
318
		$proxy_ips = config_item('proxy_ips');
319
		if ( ! empty($proxy_ips))
320
		{
321
			$proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));
322
			foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header)
323
			{
324
				if (($spoof = $this->server($header)) !== FALSE)
325
				{
326
					// Some proxies typically list the whole chain of IP
327
					// addresses through which the client has reached us.
328
					// e.g. client_ip, proxy_ip1, proxy_ip2, etc.
329
					if (strpos($spoof, ',') !== FALSE)
330
					{
331
						$spoof = explode(',', $spoof, 2);
332
						$spoof = $spoof[0];
333
					}
334
335
					if ( ! $this->valid_ip($spoof))
336
					{
337
						$spoof = FALSE;
338
					}
339
					else
340
					{
341
						break;
342
					}
343
				}
344
			}
345
346
			$this->ip_address = ($spoof !== FALSE && in_array($_SERVER['REMOTE_ADDR'], $proxy_ips, TRUE))
347
				? $spoof : $_SERVER['REMOTE_ADDR'];
348
		}
349
		else
350
		{
351
			$this->ip_address = $_SERVER['REMOTE_ADDR'];
352
		}
353
354
		if ( ! $this->valid_ip($this->ip_address))
355
		{
356
			$this->ip_address = '0.0.0.0';
357
		}
358
359
		return $this->ip_address;
360
	}
361
362
	// --------------------------------------------------------------------
363
364
	/**
365
	* Validate IP Address
366
	*
367
	* @access	public
368
	* @param	string
369
	* @param	string	ipv4 or ipv6
370
	* @return	bool
371
	*/
372
	public function valid_ip($ip, $which = '')
373
	{
374
		$which = strtolower($which);
375
376
		// First check if filter_var is available
377
		if (is_callable('filter_var'))
378
		{
379
			switch ($which) {
380
				case 'ipv4':
381
					$flag = FILTER_FLAG_IPV4;
382
					break;
383
				case 'ipv6':
384
					$flag = FILTER_FLAG_IPV6;
385
					break;
386
				default:
387
					$flag = '';
388
					break;
389
			}
390
391
			return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flag);
392
		}
393
394
		if ($which !== 'ipv6' && $which !== 'ipv4')
395
		{
396
			if (strpos($ip, ':') !== FALSE)
397
			{
398
				$which = 'ipv6';
399
			}
400
			elseif (strpos($ip, '.') !== FALSE)
401
			{
402
				$which = 'ipv4';
403
			}
404
			else
405
			{
406
				return FALSE;
407
			}
408
		}
409
410
		$func = '_valid_'.$which;
411
		return $this->$func($ip);
412
	}
413
414
	// --------------------------------------------------------------------
415
416
	/**
417
	* Validate IPv4 Address
418
	*
419
	* Updated version suggested by Geert De Deckere
420
	*
421
	* @access	protected
422
	* @param	string
423
	* @return	bool
424
	*/
425
	protected function _valid_ipv4($ip)
426
	{
427
		$ip_segments = explode('.', $ip);
428
429
		// Always 4 segments needed
430
		if (count($ip_segments) !== 4)
431
		{
432
			return FALSE;
433
		}
434
		// IP can not start with 0
435
		if ($ip_segments[0][0] == '0')
436
		{
437
			return FALSE;
438
		}
439
440
		// Check each segment
441
		foreach ($ip_segments as $segment)
442
		{
443
			// IP segments must be digits and can not be
444
			// longer than 3 digits or greater then 255
445
			if ($segment == '' OR preg_match("/[^0-9]/", $segment) OR $segment > 255 OR strlen($segment) > 3)
446
			{
447
				return FALSE;
448
			}
449
		}
450
451
		return TRUE;
452
	}
453
454
	// --------------------------------------------------------------------
455
456
	/**
457
	* Validate IPv6 Address
458
	*
459
	* @access	protected
460
	* @param	string
461
	* @return	bool
462
	*/
463
	protected function _valid_ipv6($str)
464
	{
465
		// 8 groups, separated by :
466
		// 0-ffff per group
467
		// one set of consecutive 0 groups can be collapsed to ::
468
469
		$groups = 8;
470
		$collapsed = FALSE;
471
472
		$chunks = array_filter(
473
			preg_split('/(:{1,2})/', $str, NULL, PREG_SPLIT_DELIM_CAPTURE)
474
		);
475
476
		// Rule out easy nonsense
477
		if (current($chunks) == ':' OR end($chunks) == ':')
478
		{
479
			return FALSE;
480
		}
481
482
		// PHP supports IPv4-mapped IPv6 addresses, so we'll expect those as well
483
		if (strpos(end($chunks), '.') !== FALSE)
484
		{
485
			$ipv4 = array_pop($chunks);
486
487
			if ( ! $this->_valid_ipv4($ipv4))
488
			{
489
				return FALSE;
490
			}
491
492
			$groups--;
493
		}
494
495
		while ($seg = array_pop($chunks))
496
		{
497
			if ($seg[0] == ':')
498
			{
499
				if (--$groups == 0)
500
				{
501
					return FALSE;	// too many groups
502
				}
503
504
				if (strlen($seg) > 2)
505
				{
506
					return FALSE;	// long separator
507
				}
508
509
				if ($seg == '::')
510
				{
511
					if ($collapsed)
512
					{
513
						return FALSE;	// multiple collapsed
514
					}
515
516
					$collapsed = TRUE;
517
				}
518
			}
519
			elseif (preg_match("/[^0-9a-f]/i", $seg) OR strlen($seg) > 4)
520
			{
521
				return FALSE; // invalid segment
522
			}
523
		}
524
525
		return $collapsed OR $groups == 1;
526
	}
527
528
	// --------------------------------------------------------------------
529
530
	/**
531
	* User Agent
532
	*
533
	* @access	public
534
	* @return	string
535
	*/
536
	function user_agent()
537
	{
538
		if ($this->user_agent !== FALSE)
539
		{
540
			return $this->user_agent;
541
		}
542
543
		$this->user_agent = ( ! isset($_SERVER['HTTP_USER_AGENT'])) ? FALSE : $_SERVER['HTTP_USER_AGENT'];
544
545
		return $this->user_agent;
546
	}
547
548
	// --------------------------------------------------------------------
549
550
	/**
551
	* Sanitize Globals
552
	*
553
	* This function does the following:
554
	*
555
	* Unsets $_GET data (if query strings are not enabled)
556
	*
557
	* Unsets all globals if register_globals is enabled
558
	*
559
	* Standardizes newline characters to \n
560
	*
561
	* @access	private
562
	* @return	void
563
	*/
564
	function _sanitize_globals()
565
	{
566
		// It would be "wrong" to unset any of these GLOBALS.
567
		$protected = array('_SERVER', '_GET', '_POST', '_FILES', '_REQUEST',
568
							'_SESSION', '_ENV', 'GLOBALS', 'HTTP_RAW_POST_DATA',
569
							'system_folder', 'application_folder', 'BM', 'EXT',
570
							'CFG', 'URI', 'RTR', 'OUT', 'IN');
571
572
		// Unset globals for securiy.
573
		// This is effectively the same as register_globals = off
574
		foreach (array($_GET, $_POST, $_COOKIE) as $global)
575
		{
576
			if ( ! is_array($global))
577
			{
578
				if ( ! in_array($global, $protected))
579
				{
580
					global $$global;
581
					$$global = NULL;
582
				}
583
			}
584
			else
585
			{
586
				foreach ($global as $key => $val)
587
				{
588
					if ( ! in_array($key, $protected))
589
					{
590
						global $$key;
591
						$$key = NULL;
592
					}
593
				}
594
			}
595
		}
596
597
		// Is $_GET data allowed? If not we'll set the $_GET to an empty array
598
		if ($this->_allow_get_array == FALSE)
599
		{
600
			$_GET = array();
601
		}
602
		else
603
		{
604
			if (is_array($_GET) AND count($_GET) > 0)
605
			{
606
				foreach ($_GET as $key => $val)
607
				{
608
					$_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
609
				}
610
			}
611
		}
612
613
		// Clean $_POST Data
614
		if (is_array($_POST) AND count($_POST) > 0)
615
		{
616
			foreach ($_POST as $key => $val)
617
			{
618
				$_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
619
			}
620
		}
621
622
		// Clean $_COOKIE Data
623
		if (is_array($_COOKIE) AND count($_COOKIE) > 0)
624
		{
625
			// Also get rid of specially treated cookies that might be set by a server
626
			// or silly application, that are of no use to a CI application anyway
627
			// but that when present will trip our 'Disallowed Key Characters' alarm
628
			// http://www.ietf.org/rfc/rfc2109.txt
629
			// note that the key names below are single quoted strings, and are not PHP variables
630
			unset($_COOKIE['$Version']);
631
			unset($_COOKIE['$Path']);
632
			unset($_COOKIE['$Domain']);
633
634
			foreach ($_COOKIE as $key => $val)
635
			{
636
				$_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
637
			}
638
		}
639
640
		// Sanitize PHP_SELF
641
		$_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);
642
643
644
		// CSRF Protection check on HTTP requests
645
		if ($this->_enable_csrf == TRUE && ! $this->is_cli_request())
646
		{
647
			$this->security->csrf_verify();
648
		}
649
650
		log_message('debug', "Global POST and COOKIE data sanitized");
651
	}
652
653
	// --------------------------------------------------------------------
654
655
	/**
656
	* Clean Input Data
657
	*
658
	* This is a helper function. It escapes data and
659
	* standardizes newline characters to \n
660
	*
661
	* @access	private
662
	* @param	string
663
	* @return	string
664
	*/
665
	function _clean_input_data($str)
666
	{
667
		if (is_array($str))
668
		{
669
			$new_array = array();
670
			foreach ($str as $key => $val)
671
			{
672
				$new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
673
			}
674
			return $new_array;
675
		}
676
677
		/* We strip slashes if magic quotes is on to keep things consistent
678
679
		   NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
680
			 it will probably not exist in future versions at all.
681
		*/
682
		if ( ! is_php('5.4') && get_magic_quotes_gpc())
683
		{
684
			$str = stripslashes($str);
685
		}
686
687
		// Clean UTF-8 if supported
688
		if (UTF8_ENABLED === TRUE)
689
		{
690
			$str = $this->uni->clean_string($str);
691
		}
692
693
		// Remove control characters
694
		$str = remove_invisible_characters($str);
695
696
		// Should we filter the input data?
697
		if ($this->_enable_xss === TRUE)
698
		{
699
			$str = $this->security->xss_clean($str);
700
		}
701
702
		// Standardize newlines if needed
703
		if ($this->_standardize_newlines == TRUE)
704
		{
705
			if (strpos($str, "\r") !== FALSE)
706
			{
707
				$str = str_replace(array("\r\n", "\r", "\r\n\n"), PHP_EOL, $str);
708
			}
709
		}
710
711
		return $str;
712
	}
713
714
	// --------------------------------------------------------------------
715
716
	/**
717
	* Clean Keys
718
	*
719
	* This is a helper function. To prevent malicious users
720
	* from trying to exploit keys we make sure that keys are
721
	* only named with alpha-numeric text and a few other items.
722
	*
723
	* @access	private
724
	* @param	string
725
	* @return	string
726
	*/
727
	function _clean_input_keys($str)
728
	{
729
		if ( ! preg_match("/^[a-z0-9:_\/-]+$/i", $str))
730
		{
731
			exit('Disallowed Key Characters.');
732
		}
733
734
		// Clean UTF-8 if supported
735
		if (UTF8_ENABLED === TRUE)
736
		{
737
			$str = $this->uni->clean_string($str);
738
		}
739
740
		return $str;
741
	}
742
743
	// --------------------------------------------------------------------
744
745
	/**
746
	 * Request Headers
747
	 *
748
	 * In Apache, you can simply call apache_request_headers(), however for
749
	 * people running other webservers the function is undefined.
750
	 *
751
	 * @param	bool XSS cleaning
752
	 *
753
	 * @return array
754
	 */
755
	public function request_headers($xss_clean = FALSE)
756
	{
757
		// Look at Apache go!
758
		if (function_exists('apache_request_headers'))
759
		{
760
			$headers = apache_request_headers();
761
		}
762
		else
763
		{
764
			$headers['Content-Type'] = (isset($_SERVER['CONTENT_TYPE'])) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE');
765
766
			foreach ($_SERVER as $key => $val)
767
			{
768
				if (strncmp($key, 'HTTP_', 5) === 0)
769
				{
770
					$headers[substr($key, 5)] = $this->_fetch_from_array($_SERVER, $key, $xss_clean);
771
				}
772
			}
773
		}
774
775
		// take SOME_HEADER and turn it into Some-Header
776
		foreach ($headers as $key => $val)
777
		{
778
			$key = str_replace('_', ' ', strtolower($key));
779
			$key = str_replace(' ', '-', ucwords($key));
780
781
			$this->headers[$key] = $val;
782
		}
783
784
		return $this->headers;
785
	}
786
787
	// --------------------------------------------------------------------
788
789
	/**
790
	 * Get Request Header
791
	 *
792
	 * Returns the value of a single member of the headers class member
793
	 *
794
	 * @param 	string		array key for $this->headers
795
	 * @param	boolean		XSS Clean or not
796
	 * @return 	mixed		FALSE on failure, string on success
797
	 */
798
	public function get_request_header($index, $xss_clean = FALSE)
799
	{
800
		if (empty($this->headers))
801
		{
802
			$this->request_headers();
803
		}
804
805
		if ( ! isset($this->headers[$index]))
806
		{
807
			return FALSE;
808
		}
809
810
		if ($xss_clean === TRUE)
811
		{
812
			return $this->security->xss_clean($this->headers[$index]);
813
		}
814
815
		return $this->headers[$index];
816
	}
817
818
	// --------------------------------------------------------------------
819
820
	/**
821
	 * Is ajax Request?
822
	 *
823
	 * Test to see if a request contains the HTTP_X_REQUESTED_WITH header
824
	 *
825
	 * @return 	boolean
826
	 */
827
	public function is_ajax_request()
828
	{
829
		return ($this->server('HTTP_X_REQUESTED_WITH') === 'XMLHttpRequest');
830
	}
831
832
	// --------------------------------------------------------------------
833
834
	/**
835
	 * Is cli Request?
836
	 *
837
	 * Test to see if a request was made from the command line
838
	 *
839
	 * @return 	bool
840
	 */
841
	public function is_cli_request()
842
	{
843
		return (php_sapi_name() === 'cli' OR defined('STDIN'));
844
	}
845
846
}
847
848
/* End of file Input.php */
849
/* Location: ./system/core/Input.php */