diff options
Diffstat (limited to 'system/core')
-rw-r--r-- | system/core/CodeIgniter.php | 9 | ||||
-rw-r--r-- | system/core/Common.php | 135 | ||||
-rw-r--r-- | system/core/Config.php | 28 | ||||
-rw-r--r-- | system/core/Exceptions.php | 2 | ||||
-rw-r--r-- | system/core/Input.php | 49 | ||||
-rw-r--r-- | system/core/Loader.php | 18 | ||||
-rw-r--r-- | system/core/Log.php | 3 | ||||
-rw-r--r-- | system/core/Output.php | 179 | ||||
-rw-r--r-- | system/core/Security.php | 10 |
9 files changed, 252 insertions, 181 deletions
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php index a026920a4..c12116236 100644 --- a/system/core/CodeIgniter.php +++ b/system/core/CodeIgniter.php @@ -73,11 +73,10 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * ------------------------------------------------------ */ set_error_handler('_exception_handler'); + register_shutdown_function('_shutdown_handler'); - if ( ! is_php('5.4')) - { - @ini_set('magic_quotes_runtime', 0); // Kill magic quotes - } + // Kill magic quotes + is_php('5.4') OR @ini_set('magic_quotes_runtime', 0); /* * ------------------------------------------------------ @@ -88,7 +87,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * The subclass prefix allows CI to know if a core class is * being extended via a library in the local application * "libraries" folder. Since CI allows config items to be - * overriden via data set in the main index. php file, + * overriden via data set in the main index.php file, * before proceeding we need to know if a subclass_prefix * override exists. If so, we will set this value now, * before any classes are loaded diff --git a/system/core/Common.php b/system/core/Common.php index 21e1df9c6..56008efe8 100644 --- a/system/core/Common.php +++ b/system/core/Common.php @@ -82,7 +82,7 @@ if ( ! function_exists('is_really_writable')) function is_really_writable($file) { // If we're on a Unix server with safe_mode off we call is_writable - if (DIRECTORY_SEPARATOR === '/' && (bool) @ini_get('safe_mode') === FALSE) + if (DIRECTORY_SEPARATOR === '/' && (is_php('5.4') OR (bool) @ini_get('safe_mode') === FALSE)) { return is_writable($file); } @@ -224,56 +224,51 @@ if ( ! function_exists('get_config')) * @param array * @return array */ - function &get_config($replace = array()) + function &get_config(Array $replace = array()) { static $_config; - if (isset($_config)) + if (empty($_config)) { - return $_config[0]; - } + $file_path = APPPATH.'config/config.php'; + $found = FALSE; + if (file_exists($file_path)) + { + $found = TRUE; + require($file_path); + } - $file_path = APPPATH.'config/config.php'; - $found = FALSE; - if (file_exists($file_path)) - { - $found = TRUE; - require($file_path); - } + // Is the config file in the environment folder? + if (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php')) + { + require($file_path); + } + elseif ( ! $found) + { + set_status_header(503); + echo 'The configuration file does not exist.'; + exit(EXIT_CONFIG); + } - // Is the config file in the environment folder? - if (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php')) - { - require($file_path); - } - elseif ( ! $found) - { - set_status_header(503); - echo 'The configuration file does not exist.'; - exit(EXIT_CONFIG); - } + // Does the $config array exist in the file? + if ( ! isset($config) OR ! is_array($config)) + { + set_status_header(503); + echo 'Your config file does not appear to be formatted correctly.'; + exit(EXIT_CONFIG); + } - // Does the $config array exist in the file? - if ( ! isset($config) OR ! is_array($config)) - { - set_status_header(503); - echo 'Your config file does not appear to be formatted correctly.'; - exit(EXIT_CONFIG); + // references cannot be directly assigned to static variables, so we use an array + $_config[0] =& $config; } - // Are any values being dynamically replaced? - if (count($replace) > 0) + // Are any values being dynamically added or replaced? + foreach ($replace as $key => $val) { - foreach ($replace as $key => $val) - { - if (isset($config[$key])) - { - $config[$key] = $val; - } - } + $_config[0][$key] = $val; } - return $_config[0] =& $config; + return $_config[0]; } } @@ -434,10 +429,9 @@ if ( ! function_exists('log_message')) * * @param string the error level: 'error', 'debug' or 'info' * @param string the error message - * @param bool whether the error is a native PHP error * @return void */ - function log_message($level, $message, $php_error = FALSE) + function log_message($level, $message) { static $_log; @@ -447,7 +441,7 @@ if ( ! function_exists('log_message')) $_log[0] =& load_class('Log', 'core'); } - $_log[0]->write_log($level, $message, $php_error); + $_log[0]->write_log($level, $message); } } @@ -555,14 +549,27 @@ if ( ! function_exists('_exception_handler')) * to display errors based on the current error_reporting level. * We do that with the use of a PHP error template. * - * @param int - * @param string - * @param string - * @param int + * @param int $severity + * @param string $message + * @param string $filepath + * @param int $line * @return void */ function _exception_handler($severity, $message, $filepath, $line) { + $is_error = (((E_ERROR | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $severity) === $severity); + + // When an error occurred, set the status header to '500 Internal Server Error' + // to indicate to the client something went wrong. + // This can't be done within the $_error->show_php_error method because + // it is only called when the display_errors flag is set (which isn't usually + // the case in a production environment) or when errors are ignored because + // they are above the error_reporting threshold. + if ($is_error) + { + set_status_header(500); + } + $_error =& load_class('Exceptions', 'core'); // Should we ignore the error? We'll get the current error_reporting @@ -579,6 +586,42 @@ if ( ! function_exists('_exception_handler')) } $_error->log_exception($severity, $message, $filepath, $line); + + // If the error is fatal, the execution of the script should be stopped because + // errors can't be recovered from. Halting the script conforms with PHP's + // default error handling. See http://www.php.net/manual/en/errorfunc.constants.php + if ($is_error) + { + exit(EXIT_ERROR); + } + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('_shutdown_handler')) +{ + /** + * Shutdown Handler + * + * This is the shutdown handler that is declared at the top + * of CodeIgniter.php. The main reason we use this is to simulate + * a complete custom exception handler. + * + * E_STRICT is purposivly neglected because such events may have + * been caught. Duplication or none? None is preferred for now. + * + * @link http://insomanic.me.uk/post/229851073/php-trick-catching-fatal-errors-e-error-with-a + * @return void + */ + function _shutdown_handler() + { + $last_error = function_exists('error_get_last') ? error_get_last() : NULL; + if (isset($last_error) && + ($last_error['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING))) + { + _exception_handler($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); + } } } diff --git a/system/core/Config.php b/system/core/Config.php index 109ee6424..a0e830abe 100644 --- a/system/core/Config.php +++ b/system/core/Config.php @@ -228,13 +228,21 @@ class CI_Config { * @uses CI_Config::_uri_string() * * @param string|string[] $uri URI string or an array of segments + * @param string $protocol * @return string */ - public function site_url($uri = '') + public function site_url($uri = '', $protocol = NULL) { + $base_url = $this->slash_item('base_url'); + + if (isset($protocol)) + { + $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + } + if (empty($uri)) { - return $this->slash_item('base_url').$this->item('index_page'); + return $base_url.$this->item('index_page'); } $uri = $this->_uri_string($uri); @@ -255,14 +263,14 @@ class CI_Config { } } - return $this->slash_item('base_url').$this->slash_item('index_page').$uri; + return $base_url.$this->slash_item('index_page').$uri; } elseif (strpos($uri, '?') === FALSE) { $uri = '?'.$uri; } - return $this->slash_item('base_url').$this->item('index_page').$uri; + return $base_url.$this->item('index_page').$uri; } // ------------------------------------------------------------- @@ -275,11 +283,19 @@ class CI_Config { * @uses CI_Config::_uri_string() * * @param string|string[] $uri URI string or an array of segments + * @param string $protocol * @return string */ - public function base_url($uri = '') + public function base_url($uri = '', $protocol = NULL) { - return $this->slash_item('base_url').ltrim($this->_uri_string($uri), '/'); + $base_url = $this->slash_item('base_url'); + + if (isset($protocol)) + { + $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + } + + return $base_url.ltrim($this->_uri_string($uri), '/'); } // ------------------------------------------------------------- diff --git a/system/core/Exceptions.php b/system/core/Exceptions.php index 9c68d06a5..d7e5ed4d9 100644 --- a/system/core/Exceptions.php +++ b/system/core/Exceptions.php @@ -91,7 +91,7 @@ class CI_Exceptions { public function log_exception($severity, $message, $filepath, $line) { $severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity; - log_message('error', 'Severity: '.$severity.' --> '.$message. ' '.$filepath.' '.$line, TRUE); + log_message('error', 'Severity: '.$severity.' --> '.$message. ' '.$filepath.' '.$line); } // -------------------------------------------------------------------- diff --git a/system/core/Input.php b/system/core/Input.php index 1e67ce183..8c32e459e 100644 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -261,7 +261,7 @@ class CI_Input { * @param bool $xss_clean Whether to apply XSS filtering * @return mixed */ - public function get_post($index = '', $xss_clean = FALSE) + public function post_get($index = '', $xss_clean = FALSE) { return isset($_POST[$index]) ? $this->post($index, $xss_clean) @@ -271,6 +271,22 @@ class CI_Input { // -------------------------------------------------------------------- /** + * Fetch an item from GET data with fallback to POST + * + * @param string $index Index for item to be fetched from $_GET or $_POST + * @param bool $xss_clean Whether to apply XSS filtering + * @return mixed + */ + public function get_post($index = '', $xss_clean = FALSE) + { + return isset($_GET[$index]) + ? $this->get($index, $xss_clean) + : $this->post($index, $xss_clean); + } + + // -------------------------------------------------------------------- + + /** * Fetch an item from the COOKIE array * * @param string $index Index for item to be fetched from $_COOKIE @@ -677,7 +693,14 @@ class CI_Input { foreach ($_COOKIE as $key => $val) { - $_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); + if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE) + { + $_COOKIE[$cookie_key] = $this->_clean_input_data($val); + } + else + { + unset($_COOKIE[$key]); + } } } @@ -690,7 +713,7 @@ class CI_Input { $this->security->csrf_verify(); } - log_message('debug', 'Global POST and COOKIE data sanitized'); + log_message('debug', 'Global POST, GET and COOKIE data sanitized'); } // -------------------------------------------------------------------- @@ -760,15 +783,25 @@ class CI_Input { * only named with alpha-numeric text and a few other items. * * @param string $str Input string - * @return string + * @param string $fatal Whether to terminate script exection + * or to return FALSE if an invalid + * key is encountered + * @return string|bool */ - protected function _clean_input_keys($str) + protected function _clean_input_keys($str, $fatal = TRUE) { if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str)) { - set_status_header(503); - echo 'Disallowed Key Characters.'; - exit(EXIT_USER_INPUT); + if ($fatal === TRUE) + { + return FALSE; + } + else + { + set_status_header(503); + echo 'Disallowed Key Characters.'; + exit(EXIT_USER_INPUT); + } } // Clean UTF-8 if supported diff --git a/system/core/Loader.php b/system/core/Loader.php index 1709c2db1..70c1e4154 100644 --- a/system/core/Loader.php +++ b/system/core/Loader.php @@ -222,7 +222,7 @@ class CI_Loader { { foreach ($model as $key => $value) { - $this->model(is_int($key) ? $value : $key, $value); + is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn); } return; } @@ -415,7 +415,7 @@ class CI_Loader { * to be extracted for use in the view * @param bool $return Whether to return the view output * or leave it to the Output class - * @return void + * @return void|string */ public function view($view, $vars = array(), $return = FALSE) { @@ -471,6 +471,20 @@ class CI_Loader { // -------------------------------------------------------------------- /** + * Clear Cached Variables + * + * Clears the cached variables. + * + * @return void + */ + public function clear_vars() + { + $this->_ci_cached_vars = array(); + } + + // -------------------------------------------------------------------- + + /** * Get Variable * * Check if a variable is set and retrieve it. diff --git a/system/core/Log.php b/system/core/Log.php index e4d72b544..b2327b8f0 100644 --- a/system/core/Log.php +++ b/system/core/Log.php @@ -140,10 +140,9 @@ class CI_Log { * * @param string the error level: 'error', 'debug' or 'info' * @param string the error message - * @param bool whether the error is a native PHP error * @return bool */ - public function write_log($level, $msg, $php_error = FALSE) + public function write_log($level, $msg) { if ($this->_enabled === FALSE) { diff --git a/system/core/Output.php b/system/core/Output.php index 06d7a866b..5173f7ed8 100644 --- a/system/core/Output.php +++ b/system/core/Output.php @@ -740,13 +740,13 @@ class CI_Output { preg_match_all('{<style.+</style>}msU', $output, $style_clean); foreach ($style_clean[0] as $s) { - $output = str_replace($s, $this->_minify_script_style($s, TRUE), $output); + $output = str_replace($s, $this->_minify_js_css($s, 'css', TRUE), $output); } // Minify the javascript in <script> tags. foreach ($javascript_clean[0] as $s) { - $javascript_mini[] = $this->_minify_script_style($s, TRUE); + $javascript_mini[] = $this->_minify_js_css($s, 'js', TRUE); } // Replace multiple spaces with a single space. @@ -792,13 +792,14 @@ class CI_Output { break; case 'text/css': + + return $this->_minify_js_css($output, 'css'); + case 'text/javascript': case 'application/javascript': case 'application/x-javascript': - $output = $this->_minify_script_style($output); - - break; + return $this->_minify_js_css($output, 'js'); default: break; } @@ -809,134 +810,100 @@ class CI_Output { // -------------------------------------------------------------------- /** - * Minify Style and Script - * - * Reduce excessive size of CSS/JavaScript content. To remove spaces this - * script walks the string as an array and determines if the pointer is inside - * a string created by single quotes or double quotes. spaces inside those - * strings are not stripped. Opening and closing tags are severed from - * the string initially and saved without stripping whitespace to preserve - * the tags and any associated properties if tags are present + * Minify JavaScript and CSS code * - * Minification logic/workflow is similar to methods used by Douglas Crockford - * in JSMIN. http://www.crockford.com/javascript/jsmin.html + * Strips comments and excessive whitespace characters * - * KNOWN ISSUE: ending a line with a closing parenthesis ')' and no semicolon - * where there should be one will break the Javascript. New lines after a - * closing parenthesis are not recognized by the script. For best results - * be sure to terminate lines with a semicolon when appropriate. - * - * @param string $output Output to minify - * @param bool $has_tags Specify if the output has style or script tags - * @return string Minified output + * @param string $output + * @param string $type 'js' or 'css' + * @param bool $tags Whether $output contains the 'script' or 'style' tag + * @return string */ - protected function _minify_script_style($output, $has_tags = FALSE) + protected function _minify_js_css($output, $type, $tags = FALSE) { - // We only need this if there are tags in the file - if ($has_tags === TRUE) + if ($tags === TRUE) { - // Remove opening tag and save for later - $pos = strpos($output, '>') + 1; - $open_tag = substr($output, 0, $pos); - $output = substr_replace($output, '', 0, $pos); + $tags = array('close' => strrchr($output, '<')); - // Remove closing tag and save it for later - $pos = strpos($output, '</'); - $closing_tag = substr($output, $pos, strlen($output)); - $output = substr_replace($output, '', $pos); - } + $open_length = strpos($output, '>') + 1; + $tags['open'] = substr($output, 0, $open_length); - // Remove CSS comments - $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!i', '', $output); + $output = substr($output, $open_length, -strlen($tags['close'])); - // Remove spaces around curly brackets, colons, - // semi-colons, parenthesis, commas - $chunks = preg_split('/([\'|"]).+(?![^\\\]\\1)\\1/iU', $output, -1, PREG_SPLIT_OFFSET_CAPTURE); - for ($i = count($chunks) - 1; $i >= 0; $i--) - { - $output = substr_replace( - $output, - preg_replace('/\s*(:|;|,|}|{|\(|\))\s*/i', '$1', $chunks[$i][0]), - $chunks[$i][1], - strlen($chunks[$i][0]) - ); + // Strip spaces from the tags + $tags = preg_replace('#\s{2,}#', ' ', $tags); } - // Replace tabs with spaces - // Replace carriage returns & multiple new lines with single new line - // and trim any leading or trailing whitespace - $output = trim(preg_replace(array('/\t+/', '/\r/', '/\n+/'), array(' ', "\n", "\n"), $output)); + $output = trim($output); - // Remove spaces when safe to do so. - $in_string = $in_dstring = $prev = FALSE; - $array_output = str_split($output); - foreach ($array_output as $key => $value) + if ($type === 'js') { - if ($in_string === FALSE && $in_dstring === FALSE) + // Catch all string literals and comment blocks + if (preg_match_all('#((?:((?<!\\\)\'|")|(/\*)|(//)).*(?(2)(?<!\\\)\2|(?(3)\*/|\n)))#msuUS', $output, $match, PREG_OFFSET_CAPTURE)) { - if ($value === ' ') + $js_literals = $js_code = array(); + for ($match = $match[0], $c = count($match), $i = $pos = $offset = 0; $i < $c; $i++) { - // Get the next element in the array for comparisons - $next = $array_output[$key + 1]; - - // Strip spaces preceded/followed by a non-ASCII character - // or not preceded/followed by an alphanumeric - // or not preceded/followed \ $ and _ - if ((preg_match('/^[\x20-\x7f]*$/D', $next) OR preg_match('/^[\x20-\x7f]*$/D', $prev)) - && ( ! ctype_alnum($next) OR ! ctype_alnum($prev)) - && ! in_array($next, array('\\', '_', '$'), TRUE) - && ! in_array($prev, array('\\', '_', '$'), TRUE) - ) + $js_code[$pos++] = trim(substr($output, $offset, $match[$i][1] - $offset)); + $offset = $match[$i][1] + strlen($match[$i][0]); + + // Save only if we haven't matched a comment block + if ($match[$i][0][0] !== '/') { - unset($array_output[$key]); + $js_literals[$pos++] = array_shift($match[$i]); } } - else - { - // Save this value as previous for the next iteration - // if it is not a blank space - $prev = $value; - } - } + $js_code[$pos] = substr($output, $offset); - if ($value === "'") - { - $in_string = ! $in_string; + // $match might be quite large, so free it up together with other vars that we no longer need + unset($match, $offset, $pos); } - elseif ($value === '"') + else { - $in_dstring = ! $in_dstring; + $js_code = array($output); + $js_literals = array(); } + + $varname = 'js_code'; + } + else + { + $varname = 'output'; } - // Put the string back together after spaces have been stripped - $output = implode($array_output); + // Standartize new lines + $$varname = str_replace(array("\r\n", "\r"), "\n", $$varname); - // Remove new line characters unless previous or next character is - // printable or Non-ASCII - preg_match_all('/[\n]/', $output, $lf, PREG_OFFSET_CAPTURE); - $removed_lf = 0; - foreach ($lf as $feed_position) + if ($type === 'js') { - foreach ($feed_position as $position) - { - $position = $position[1] - $removed_lf; - $next = $output[$position + 1]; - $prev = $output[$position - 1]; - if ( ! ctype_print($next) && ! ctype_print($prev) - && ! preg_match('/^[\x20-\x7f]*$/D', $next) - && ! preg_match('/^[\x20-\x7f]*$/D', $prev) - ) - { - $output = substr_replace($output, '', $position, 1); - $removed_lf++; - } - } + $patterns = array( + '#\s*([!\#%&()*+,\-./:;<=>?@\[\]^`{|}~])\s*#' => '$1', // Remove spaces following and preceeding JS-wise non-special & non-word characters + '#\s{2,}#' => ' ' // Reduce the remaining multiple whitespace characters to a single space + ); + } + else + { + $patterns = array( + '#/\*.*(?=\*/)\*/#s' => '', // Remove /* block comments */ + '#\n?//[^\n]*#' => '', // Remove // line comments + '#\s*([^\w.\#%])\s*#U' => '$1', // Remove spaces following and preceeding non-word characters, excluding dots, hashes and the percent sign + '#\s{2,}#' => ' ' // Reduce the remaining multiple space characters to a single space + ); + } + + $$varname = preg_replace(array_keys($patterns), array_values($patterns), $$varname); + + // Glue back JS quoted strings + if ($type === 'js') + { + $js_code += $js_literals; + ksort($js_code); + $output = implode($js_code); + unset($js_code, $js_literals, $varname, $patterns); } - // Put the opening and closing tags back if applicable - return isset($open_tag) - ? $open_tag.$output.$closing_tag + return is_array($tags) + ? $tags['open'].$output.$tags['close'] : $output; } diff --git a/system/core/Security.php b/system/core/Security.php index 70cf3e013..9423f825c 100644 --- a/system/core/Security.php +++ b/system/core/Security.php @@ -553,9 +553,9 @@ class CI_Security { { $matches = $matches1 = 0; + $str = preg_replace('~(�*[0-9a-f]{2,5});?~iS', '$1;', $str, -1, $matches); + $str = preg_replace('~(&#\d{2,4});?~S', '$1;', $str, -1, $matches1); $str = html_entity_decode($str, ENT_COMPAT, $charset); - $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str, -1, $matches); - $str = preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str, -1, $matches1); } while ($matches OR $matches1); @@ -603,7 +603,7 @@ class CI_Security { */ public function strip_image_tags($str) { - return preg_replace(array('#<img\s+.*?src\s*=\s*["\'](.+?)["\'].*?\>#', '#<img\s+.*?src\s*=\s*(.+?).*?\>#'), '\\1', $str); + return preg_replace(array('#<img[\s/]+.*?src\s*=\s*["\'](.+?)["\'].*?\>#', '#<img[\s/]+.*?src\s*=\s*(.+?).*?\>#'), '\\1', $str); } // ---------------------------------------------------------------- @@ -884,7 +884,7 @@ class CI_Security { { if ($this->_csrf_hash === '') { - // If the cookie exists we will use it's value. + // If the cookie exists we will use its value. // We don't necessarily want to regenerate it with // each page load since a page could contain embedded // sub-pages causing this feature to fail @@ -894,7 +894,7 @@ class CI_Security { return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name]; } - $this->_csrf_hash = md5(uniqid(rand(), TRUE)); + $this->_csrf_hash = md5(uniqid(mt_rand(), TRUE)); $this->csrf_set_cookie(); } |