Input.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. <?php
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP
  6. *
  7. * This content is released under the MIT License (MIT)
  8. *
  9. * Copyright (c) 2014 - 2016, British Columbia Institute of Technology
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in
  19. * all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27. * THE SOFTWARE.
  28. *
  29. * @package CodeIgniter
  30. * @author EllisLab Dev Team
  31. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
  32. * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
  33. * @license http://opensource.org/licenses/MIT MIT License
  34. * @link https://codeigniter.com
  35. * @since Version 1.0.0
  36. * @filesource
  37. */
  38. defined('BASEPATH') OR exit('No direct script access allowed');
  39. /**
  40. * Input Class
  41. *
  42. * Pre-processes global input data for security
  43. *
  44. * @package CodeIgniter
  45. * @subpackage Libraries
  46. * @category Input
  47. * @author EllisLab Dev Team
  48. * @link https://codeigniter.com/user_guide/libraries/input.html
  49. */
  50. class CI_Input
  51. {
  52. /**
  53. * IP address of the current user
  54. *
  55. * @var string
  56. */
  57. protected $ip_address = FALSE;
  58. /**
  59. * Allow GET array flag
  60. *
  61. * If set to FALSE, then $_GET will be set to an empty array.
  62. *
  63. * @var bool
  64. */
  65. protected $_allow_get_array = TRUE;
  66. /**
  67. * Standardize new lines flag
  68. *
  69. * If set to TRUE, then newlines are standardized.
  70. *
  71. * @var bool
  72. */
  73. protected $_standardize_newlines;
  74. /**
  75. * Enable XSS flag
  76. *
  77. * Determines whether the XSS filter is always active when
  78. * GET, POST or COOKIE data is encountered.
  79. * Set automatically based on config setting.
  80. *
  81. * @var bool
  82. */
  83. protected $_enable_xss = FALSE;
  84. /**
  85. * Enable CSRF flag
  86. *
  87. * Enables a CSRF cookie token to be set.
  88. * Set automatically based on config setting.
  89. *
  90. * @var bool
  91. */
  92. protected $_enable_csrf = FALSE;
  93. /**
  94. * List of all HTTP request headers
  95. *
  96. * @var array
  97. */
  98. protected $headers = array();
  99. /**
  100. * Raw input stream data
  101. *
  102. * Holds a cache of php://input contents
  103. *
  104. * @var string
  105. */
  106. protected $_raw_input_stream;
  107. /**
  108. * Parsed input stream data
  109. *
  110. * Parsed from php://input at runtime
  111. *
  112. * @see CI_Input::input_stream()
  113. * @var array
  114. */
  115. protected $_input_stream;
  116. protected $security;
  117. protected $uni;
  118. // --------------------------------------------------------------------
  119. /**
  120. * Class constructor
  121. *
  122. * Determines whether to globally enable the XSS processing
  123. * and whether to allow the $_GET array.
  124. *
  125. * @return void
  126. */
  127. public function __construct()
  128. {
  129. $this->_allow_get_array = (config_item('allow_get_array') === TRUE);
  130. $this->_enable_xss = (config_item('global_xss_filtering') === TRUE);
  131. $this->_enable_csrf = (config_item('csrf_protection') === TRUE);
  132. $this->_standardize_newlines = (bool)config_item('standardize_newlines');
  133. $this->security =& load_class('Security', 'core');
  134. // Do we need the UTF-8 class?
  135. if (UTF8_ENABLED === TRUE) {
  136. $this->uni =& load_class('Utf8', 'core');
  137. }
  138. // Sanitize global arrays
  139. $this->_sanitize_globals();
  140. // CSRF Protection check
  141. if ($this->_enable_csrf === TRUE && !is_cli()) {
  142. $this->security->csrf_verify();
  143. }
  144. log_message('info', 'Input Class Initialized');
  145. }
  146. // --------------------------------------------------------------------
  147. /**
  148. * Fetch from array
  149. *
  150. * Internal method used to retrieve values from global arrays.
  151. *
  152. * @param array &$array $_GET, $_POST, $_COOKIE, $_SERVER, etc.
  153. * @param mixed $index Index for item to be fetched from $array
  154. * @param bool $xss_clean Whether to apply XSS filtering
  155. * @return mixed
  156. */
  157. protected function _fetch_from_array(&$array, $index = NULL, $xss_clean = NULL)
  158. {
  159. is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
  160. // If $index is NULL, it means that the whole $array is requested
  161. isset($index) OR $index = array_keys($array);
  162. // allow fetching multiple keys at once
  163. if (is_array($index)) {
  164. $output = array();
  165. foreach ($index as $key) {
  166. $output[$key] = $this->_fetch_from_array($array, $key, $xss_clean);
  167. }
  168. return $output;
  169. }
  170. if (isset($array[$index])) {
  171. $value = $array[$index];
  172. } elseif (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) // Does the index contain array notation
  173. {
  174. $value = $array;
  175. for ($i = 0; $i < $count; $i++) {
  176. $key = trim($matches[0][$i], '[]');
  177. if ($key === '') // Empty notation will return the value as array
  178. {
  179. break;
  180. }
  181. if (isset($value[$key])) {
  182. $value = $value[$key];
  183. } else {
  184. return NULL;
  185. }
  186. }
  187. } else {
  188. return NULL;
  189. }
  190. return ($xss_clean === TRUE)
  191. ? $this->clean_xss($this->security->xss_clean($value))
  192. : $value;
  193. }
  194. /**
  195. * 过滤XSS
  196. * @param $val
  197. */
  198. function clean_xss($val)
  199. {
  200. if (!is_array($val)) {
  201. $args_arr=array(
  202. 'xss'=>"[\\'\\\"\\;\\*\\<\\>].*\\bon[a-zA-Z]{3,15}[\\s\\r\\n\\v\\f]*\\=|\\b(?:expression)\\(|\\<script[\\s\\\\\\/]|\\<\\!\\[cdata\\[|\\b(?:eval|alert|prompt|msgbox)\\s*\\(|url\\((?:\\#|data|javascript)",
  203. 'sql'=>"[^\\{\\s]{1}(\\s|\\b)+(?:select\\b|update\\b|insert(?:(\\/\\*.*?\\*\\/)|(\\s)|(\\+))+into\\b).+?(?:from\\b|set\\b)|[^\\{\\s]{1}(\\s|\\b)+(?:create|delete|drop|truncate|rename|desc)(?:(\\/\\*.*?\\*\\/)|(\\s)|(\\+))+(?:table\\b|from\\b|database\\b)|into(?:(\\/\\*.*?\\*\\/)|\\s|\\+)+(?:dump|out)file\\b|\\bsleep\\([\\s]*[\\d]+[\\s]*\\)|benchmark\\(([^\\,]*)\\,([^\\,]*)\\)|(?:declare|set|select)\\b.*@|union\\b.*(?:select|all)\\b|(?:select|update|insert|create|delete|drop|grant|truncate|rename|exec|desc|from|table|database|set|where)\\b.*(charset|ascii|bin|char|uncompress|concat|concat_ws|conv|export_set|hex|instr|left|load_file|locate|mid|sub|substring|oct|reverse|right|unhex)\\(|(?:master\\.\\.sysdatabases|msysaccessobjects|msysqueries|sysmodules|mysql\\.db|sys\\.database_name|information_schema\\.|sysobjects|sp_makewebtask|xp_cmdshell|sp_oamethod|sp_addextendedproc|sp_oacreate|xp_regread|sys\\.dbms_export_extension)",
  204. 'other'=>"\\.\\.[\\\\\\/].*\\%00([^0-9a-fA-F]|$)|%00[\\'\\\"\\.]");
  205. foreach($args_arr as $key=>$value) {
  206. if (preg_match("/" . $value . "/is", $val) == 1 || preg_match("/" . $value . "/is", urlencode($val)) == 1) {
  207. show_error("禁止访问!请勿提交非法数据!<a href='javascript:history.go(-1)'>返回</a>",403);
  208. }
  209. }
  210. return htmlspecialchars($val);
  211. } else {
  212. return $val;
  213. }
  214. }
  215. // --------------------------------------------------------------------
  216. /**
  217. * Fetch an item from the GET array
  218. *
  219. * @param mixed $index Index for item to be fetched from $_GET
  220. * @param bool $xss_clean Whether to apply XSS filtering
  221. * @return mixed
  222. */
  223. public function get($index = NULL, $xss_clean = NULL)
  224. {
  225. return $this->_fetch_from_array($_GET, $index, $xss_clean);
  226. }
  227. // --------------------------------------------------------------------
  228. /**
  229. * Fetch an item from the POST array
  230. *
  231. * @param mixed $index Index for item to be fetched from $_POST
  232. * @param bool $xss_clean Whether to apply XSS filtering
  233. * @return mixed
  234. */
  235. public function post($index = NULL, $xss_clean = NULL)
  236. {
  237. return $this->_fetch_from_array($_POST, $index, $xss_clean);
  238. }
  239. // --------------------------------------------------------------------
  240. /**
  241. * Fetch an item from POST data with fallback to GET
  242. *
  243. * @param string $index Index for item to be fetched from $_POST or $_GET
  244. * @param bool $xss_clean Whether to apply XSS filtering
  245. * @return mixed
  246. */
  247. public function post_get($index, $xss_clean = NULL)
  248. {
  249. return isset($_POST[$index])
  250. ? $this->post($index, $xss_clean)
  251. : $this->get($index, $xss_clean);
  252. }
  253. // --------------------------------------------------------------------
  254. /**
  255. * Fetch an item from GET data with fallback to POST
  256. *
  257. * @param string $index Index for item to be fetched from $_GET or $_POST
  258. * @param bool $xss_clean Whether to apply XSS filtering
  259. * @return mixed
  260. */
  261. public function get_post($index, $xss_clean = NULL)
  262. {
  263. return isset($_GET[$index])
  264. ? $this->get($index, $xss_clean)
  265. : $this->post($index, $xss_clean);
  266. }
  267. // --------------------------------------------------------------------
  268. /**
  269. * Fetch an item from the COOKIE array
  270. *
  271. * @param mixed $index Index for item to be fetched from $_COOKIE
  272. * @param bool $xss_clean Whether to apply XSS filtering
  273. * @return mixed
  274. */
  275. public function cookie($index = NULL, $xss_clean = NULL)
  276. {
  277. return $this->_fetch_from_array($_COOKIE, $index, $xss_clean);
  278. }
  279. // --------------------------------------------------------------------
  280. /**
  281. * Fetch an item from the SERVER array
  282. *
  283. * @param mixed $index Index for item to be fetched from $_SERVER
  284. * @param bool $xss_clean Whether to apply XSS filtering
  285. * @return mixed
  286. */
  287. public function server($index, $xss_clean = NULL)
  288. {
  289. return $this->_fetch_from_array($_SERVER, $index, $xss_clean);
  290. }
  291. // ------------------------------------------------------------------------
  292. /**
  293. * Fetch an item from the php://input stream
  294. *
  295. * Useful when you need to access PUT, DELETE or PATCH request data.
  296. *
  297. * @param string $index Index for item to be fetched
  298. * @param bool $xss_clean Whether to apply XSS filtering
  299. * @return mixed
  300. */
  301. public function input_stream($index = NULL, $xss_clean = NULL)
  302. {
  303. // Prior to PHP 5.6, the input stream can only be read once,
  304. // so we'll need to check if we have already done that first.
  305. if (!is_array($this->_input_stream)) {
  306. // $this->raw_input_stream will trigger __get().
  307. parse_str($this->raw_input_stream, $this->_input_stream);
  308. is_array($this->_input_stream) OR $this->_input_stream = array();
  309. }
  310. return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean);
  311. }
  312. // ------------------------------------------------------------------------
  313. /**
  314. * Set cookie
  315. *
  316. * Accepts an arbitrary number of parameters (up to 7) or an associative
  317. * array in the first parameter containing all the values.
  318. *
  319. * @param string|mixed[] $name Cookie name or an array containing parameters
  320. * @param string $value Cookie value
  321. * @param int $expire Cookie expiration time in seconds
  322. * @param string $domain Cookie domain (e.g.: '.yourdomain.com')
  323. * @param string $path Cookie path (default: '/')
  324. * @param string $prefix Cookie name prefix
  325. * @param bool $secure Whether to only transfer cookies via SSL
  326. * @param bool $httponly Whether to only makes the cookie accessible via HTTP (no javascript)
  327. * @return void
  328. */
  329. public function set_cookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE, $httponly = FALSE)
  330. {
  331. if (is_array($name)) {
  332. // always leave 'name' in last place, as the loop will break otherwise, due to $$item
  333. foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'name') as $item) {
  334. if (isset($name[$item])) {
  335. $$item = $name[$item];
  336. }
  337. }
  338. }
  339. if ($prefix === '' && config_item('cookie_prefix') !== '') {
  340. $prefix = config_item('cookie_prefix');
  341. }
  342. if ($domain == '' && config_item('cookie_domain') != '') {
  343. $domain = config_item('cookie_domain');
  344. }
  345. if ($path === '/' && config_item('cookie_path') !== '/') {
  346. $path = config_item('cookie_path');
  347. }
  348. if ($secure === FALSE && config_item('cookie_secure') === TRUE) {
  349. $secure = config_item('cookie_secure');
  350. }
  351. if ($httponly === FALSE && config_item('cookie_httponly') !== FALSE) {
  352. $httponly = config_item('cookie_httponly');
  353. }
  354. if (!is_numeric($expire)) {
  355. $expire = time() - 86500;
  356. } else {
  357. $expire = ($expire > 0) ? time() + $expire : 0;
  358. }
  359. setcookie($prefix . $name, $value, $expire, $path, $domain, $secure, $httponly);
  360. }
  361. // --------------------------------------------------------------------
  362. /**
  363. * Fetch the IP Address
  364. *
  365. * Determines and validates the visitor's IP address.
  366. *
  367. * @return string IP address
  368. */
  369. public function ip_address()
  370. {
  371. if ($this->ip_address !== FALSE) {
  372. return $this->ip_address;
  373. }
  374. $proxy_ips = config_item('proxy_ips');
  375. if (!empty($proxy_ips) && !is_array($proxy_ips)) {
  376. $proxy_ips = explode(',', str_replace(' ', '', $proxy_ips));
  377. }
  378. $this->ip_address = $this->server('REMOTE_ADDR');
  379. if ($proxy_ips) {
  380. foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header) {
  381. if (($spoof = $this->server($header)) !== NULL) {
  382. // Some proxies typically list the whole chain of IP
  383. // addresses through which the client has reached us.
  384. // e.g. client_ip, proxy_ip1, proxy_ip2, etc.
  385. sscanf($spoof, '%[^,]', $spoof);
  386. if (!$this->valid_ip($spoof)) {
  387. $spoof = NULL;
  388. } else {
  389. break;
  390. }
  391. }
  392. }
  393. if ($spoof) {
  394. for ($i = 0, $c = count($proxy_ips); $i < $c; $i++) {
  395. // Check if we have an IP address or a subnet
  396. if (strpos($proxy_ips[$i], '/') === FALSE) {
  397. // An IP address (and not a subnet) is specified.
  398. // We can compare right away.
  399. if ($proxy_ips[$i] === $this->ip_address) {
  400. $this->ip_address = $spoof;
  401. break;
  402. }
  403. continue;
  404. }
  405. // We have a subnet ... now the heavy lifting begins
  406. isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.';
  407. // If the proxy entry doesn't match the IP protocol - skip it
  408. if (strpos($proxy_ips[$i], $separator) === FALSE) {
  409. continue;
  410. }
  411. // Convert the REMOTE_ADDR IP address to binary, if needed
  412. if (!isset($ip, $sprintf)) {
  413. if ($separator === ':') {
  414. // Make sure we're have the "full" IPv6 format
  415. $ip = explode(':',
  416. str_replace('::',
  417. str_repeat(':', 9 - substr_count($this->ip_address, ':')),
  418. $this->ip_address
  419. )
  420. );
  421. for ($j = 0; $j < 8; $j++) {
  422. $ip[$j] = intval($ip[$j], 16);
  423. }
  424. $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b';
  425. } else {
  426. $ip = explode('.', $this->ip_address);
  427. $sprintf = '%08b%08b%08b%08b';
  428. }
  429. $ip = vsprintf($sprintf, $ip);
  430. }
  431. // Split the netmask length off the network address
  432. sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen);
  433. // Again, an IPv6 address is most likely in a compressed form
  434. if ($separator === ':') {
  435. $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr));
  436. for ($j = 0; $j < 8; $j++) {
  437. $netaddr[$i] = intval($netaddr[$j], 16);
  438. }
  439. } else {
  440. $netaddr = explode('.', $netaddr);
  441. }
  442. // Convert to binary and finally compare
  443. if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0) {
  444. $this->ip_address = $spoof;
  445. break;
  446. }
  447. }
  448. }
  449. }
  450. if (!$this->valid_ip($this->ip_address)) {
  451. return $this->ip_address = '0.0.0.0';
  452. }
  453. return $this->ip_address;
  454. }
  455. // --------------------------------------------------------------------
  456. /**
  457. * Validate IP Address
  458. *
  459. * @param string $ip IP address
  460. * @param string $which IP protocol: 'ipv4' or 'ipv6'
  461. * @return bool
  462. */
  463. public function valid_ip($ip, $which = '')
  464. {
  465. switch (strtolower($which)) {
  466. case 'ipv4':
  467. $which = FILTER_FLAG_IPV4;
  468. break;
  469. case 'ipv6':
  470. $which = FILTER_FLAG_IPV6;
  471. break;
  472. default:
  473. $which = NULL;
  474. break;
  475. }
  476. return (bool)filter_var($ip, FILTER_VALIDATE_IP, $which);
  477. }
  478. // --------------------------------------------------------------------
  479. /**
  480. * Fetch User Agent string
  481. *
  482. * @return string|null User Agent string or NULL if it doesn't exist
  483. */
  484. public function user_agent($xss_clean = NULL)
  485. {
  486. return $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean);
  487. }
  488. // --------------------------------------------------------------------
  489. /**
  490. * Sanitize Globals
  491. *
  492. * Internal method serving for the following purposes:
  493. *
  494. * - Unsets $_GET data, if query strings are not enabled
  495. * - Cleans POST, COOKIE and SERVER data
  496. * - Standardizes newline characters to PHP_EOL
  497. *
  498. * @return void
  499. */
  500. protected function _sanitize_globals()
  501. {
  502. // Is $_GET data allowed? If not we'll set the $_GET to an empty array
  503. if ($this->_allow_get_array === FALSE) {
  504. $_GET = array();
  505. } elseif (is_array($_GET)) {
  506. foreach ($_GET as $key => $val) {
  507. $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
  508. }
  509. }
  510. // Clean $_POST Data
  511. if (is_array($_POST)) {
  512. foreach ($_POST as $key => $val) {
  513. $_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
  514. }
  515. }
  516. // Clean $_COOKIE Data
  517. if (is_array($_COOKIE)) {
  518. // Also get rid of specially treated cookies that might be set by a server
  519. // or silly application, that are of no use to a CI application anyway
  520. // but that when present will trip our 'Disallowed Key Characters' alarm
  521. // http://www.ietf.org/rfc/rfc2109.txt
  522. // note that the key names below are single quoted strings, and are not PHP variables
  523. unset(
  524. $_COOKIE['$Version'],
  525. $_COOKIE['$Path'],
  526. $_COOKIE['$Domain']
  527. );
  528. foreach ($_COOKIE as $key => $val) {
  529. if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE) {
  530. $_COOKIE[$cookie_key] = $this->_clean_input_data($val);
  531. } else {
  532. unset($_COOKIE[$key]);
  533. }
  534. }
  535. }
  536. // Sanitize PHP_SELF
  537. $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);
  538. log_message('debug', 'Global POST, GET and COOKIE data sanitized');
  539. }
  540. // --------------------------------------------------------------------
  541. /**
  542. * Clean Input Data
  543. *
  544. * Internal method that aids in escaping data and
  545. * standardizing newline characters to PHP_EOL.
  546. *
  547. * @param string|string[] $str Input string(s)
  548. * @return string
  549. */
  550. protected function _clean_input_data($str)
  551. {
  552. if (is_array($str)) {
  553. $new_array = array();
  554. foreach (array_keys($str) as $key) {
  555. $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($str[$key]);
  556. }
  557. return $new_array;
  558. }
  559. /* We strip slashes if magic quotes is on to keep things consistent
  560. NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
  561. it will probably not exist in future versions at all.
  562. */
  563. if (!is_php('5.4') && get_magic_quotes_gpc()) {
  564. $str = stripslashes($str);
  565. }
  566. // Clean UTF-8 if supported
  567. if (UTF8_ENABLED === TRUE) {
  568. $str = $this->uni->clean_string($str);
  569. }
  570. // Remove control characters
  571. $str = remove_invisible_characters($str, FALSE);
  572. // Standardize newlines if needed
  573. if ($this->_standardize_newlines === TRUE) {
  574. return preg_replace('/(?:\r\n|[\r\n])/', PHP_EOL, $str);
  575. }
  576. return $str;
  577. }
  578. // --------------------------------------------------------------------
  579. /**
  580. * Clean Keys
  581. *
  582. * Internal method that helps to prevent malicious users
  583. * from trying to exploit keys we make sure that keys are
  584. * only named with alpha-numeric text and a few other items.
  585. *
  586. * @param string $str Input string
  587. * @param bool $fatal Whether to terminate script exection
  588. * or to return FALSE if an invalid
  589. * key is encountered
  590. * @return string|bool
  591. */
  592. protected function _clean_input_keys($str, $fatal = TRUE)
  593. {
  594. if (!preg_match('/^[a-z0-9:_\/|-]+$/i', $str)) {
  595. if ($fatal === TRUE) {
  596. return FALSE;
  597. } else {
  598. set_status_header(503);
  599. echo 'Disallowed Key Characters.';
  600. exit(7); // EXIT_USER_INPUT
  601. }
  602. }
  603. // Clean UTF-8 if supported
  604. if (UTF8_ENABLED === TRUE) {
  605. return $this->uni->clean_string($str);
  606. }
  607. return $str;
  608. }
  609. // --------------------------------------------------------------------
  610. /**
  611. * Request Headers
  612. *
  613. * @param bool $xss_clean Whether to apply XSS filtering
  614. * @return array
  615. */
  616. public function request_headers($xss_clean = FALSE)
  617. {
  618. // If header is already defined, return it immediately
  619. if (!empty($this->headers)) {
  620. return $this->_fetch_from_array($this->headers, NULL, $xss_clean);
  621. }
  622. // In Apache, you can simply call apache_request_headers()
  623. if (function_exists('apache_request_headers')) {
  624. $this->headers = apache_request_headers();
  625. } else {
  626. isset($_SERVER['CONTENT_TYPE']) && $this->headers['Content-Type'] = $_SERVER['CONTENT_TYPE'];
  627. foreach ($_SERVER as $key => $val) {
  628. if (sscanf($key, 'HTTP_%s', $header) === 1) {
  629. // take SOME_HEADER and turn it into Some-Header
  630. $header = str_replace('_', ' ', strtolower($header));
  631. $header = str_replace(' ', '-', ucwords($header));
  632. $this->headers[$header] = $_SERVER[$key];
  633. }
  634. }
  635. }
  636. return $this->_fetch_from_array($this->headers, NULL, $xss_clean);
  637. }
  638. // --------------------------------------------------------------------
  639. /**
  640. * Get Request Header
  641. *
  642. * Returns the value of a single member of the headers class member
  643. *
  644. * @param string $index Header name
  645. * @param bool $xss_clean Whether to apply XSS filtering
  646. * @return string|null The requested header on success or NULL on failure
  647. */
  648. public function get_request_header($index, $xss_clean = FALSE)
  649. {
  650. static $headers;
  651. if (!isset($headers)) {
  652. empty($this->headers) && $this->request_headers();
  653. foreach ($this->headers as $key => $value) {
  654. $headers[strtolower($key)] = $value;
  655. }
  656. }
  657. $index = strtolower($index);
  658. if (!isset($headers[$index])) {
  659. return NULL;
  660. }
  661. return ($xss_clean === TRUE)
  662. ? $this->security->xss_clean($headers[$index])
  663. : $headers[$index];
  664. }
  665. // --------------------------------------------------------------------
  666. /**
  667. * Is AJAX request?
  668. *
  669. * Test to see if a request contains the HTTP_X_REQUESTED_WITH header.
  670. *
  671. * @return bool
  672. */
  673. public function is_ajax_request()
  674. {
  675. return (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
  676. }
  677. // --------------------------------------------------------------------
  678. /**
  679. * Is CLI request?
  680. *
  681. * Test to see if a request was made from the command line.
  682. *
  683. * @deprecated 3.0.0 Use is_cli() instead
  684. * @return bool
  685. */
  686. public function is_cli_request()
  687. {
  688. return is_cli();
  689. }
  690. // --------------------------------------------------------------------
  691. /**
  692. * Get Request Method
  693. *
  694. * Return the request method
  695. *
  696. * @param bool $upper Whether to return in upper or lower case
  697. * (default: FALSE)
  698. * @return string
  699. */
  700. public function method($upper = FALSE)
  701. {
  702. return ($upper)
  703. ? strtoupper($this->server('REQUEST_METHOD'))
  704. : strtolower($this->server('REQUEST_METHOD'));
  705. }
  706. // ------------------------------------------------------------------------
  707. /**
  708. * Magic __get()
  709. *
  710. * Allows read access to protected properties
  711. *
  712. * @param string $name
  713. * @return mixed
  714. */
  715. public function __get($name)
  716. {
  717. if ($name === 'raw_input_stream') {
  718. isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input');
  719. return $this->_raw_input_stream;
  720. } elseif ($name === 'ip_address') {
  721. return $this->ip_address;
  722. }
  723. }
  724. }