diff options
Diffstat (limited to 'system')
103 files changed, 1580 insertions, 1357 deletions
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php index d830c1829..fece65bde 100644 --- a/system/core/CodeIgniter.php +++ b/system/core/CodeIgniter.php @@ -55,7 +55,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @var string * */ - define('CI_VERSION', '3.0-dev'); + define('CI_VERSION', '3.1.0-dev'); /* * ------------------------------------------------------ diff --git a/system/core/Common.php b/system/core/Common.php index 9f509745f..3ab98cf6d 100644 --- a/system/core/Common.php +++ b/system/core/Common.php @@ -181,7 +181,7 @@ if ( ! function_exists('load_class')) // Did we find the class? if ($name === FALSE) { - // Note: We use exit() rather then show_error() in order to avoid a + // Note: We use exit() rather than show_error() in order to avoid a // self-referencing loop with the Exceptions class set_status_header(503); echo 'Unable to locate the specified class: '.$class.'.php'; @@ -492,59 +492,67 @@ if ( ! function_exists('set_status_header')) */ function set_status_header($code = 200, $text = '') { - $stati = array( - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - - 400 => 'Bad Request', - 401 => 'Unauthorized', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 422 => 'Unprocessable Entity', - - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported' - ); + if (is_cli()) + { + return; + } if (empty($code) OR ! is_numeric($code)) { show_error('Status codes must be numeric', 500); } - is_int($code) OR $code = (int) $code; - if (empty($text)) { + is_int($code) OR $code = (int) $code; + $stati = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported' + ); + if (isset($stati[$code])) { $text = $stati[$code]; @@ -669,7 +677,7 @@ if ( ! function_exists('_shutdown_handler')) * 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 + * E_STRICT is purposively 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 @@ -737,9 +745,19 @@ if ( ! function_exists('html_escape')) */ function html_escape($var, $double_encode = TRUE) { + if (empty($var)) + { + return $var; + } + if (is_array($var)) { - return array_map('html_escape', $var, array_fill(0, count($var), $double_encode)); + foreach (array_keys($var) as $key) + { + $var[$key] = html_escape($var[$key], $double_encode); + } + + return $var; } return htmlspecialchars($var, ENT_QUOTES, config_item('charset'), $double_encode); @@ -820,19 +838,9 @@ if ( ! function_exists('function_usable')) { if ( ! isset($_suhosin_func_blacklist)) { - if (extension_loaded('suhosin')) - { - $_suhosin_func_blacklist = explode(',', trim(ini_get('suhosin.executor.func.blacklist'))); - - if ( ! in_array('eval', $_suhosin_func_blacklist, TRUE) && ini_get('suhosin.executor.disable_eval')) - { - $_suhosin_func_blacklist[] = 'eval'; - } - } - else - { - $_suhosin_func_blacklist = array(); - } + $_suhosin_func_blacklist = extension_loaded('suhosin') + ? explode(',', trim(ini_get('suhosin.executor.func.blacklist'))) + : array(); } return ! in_array($function_name, $_suhosin_func_blacklist, TRUE); diff --git a/system/core/Config.php b/system/core/Config.php index a191a7727..0264776f9 100644 --- a/system/core/Config.php +++ b/system/core/Config.php @@ -88,11 +88,9 @@ class CI_Config { // Set the base_url automatically if none was provided if (empty($this->config['base_url'])) { - // The regular expression is only a basic validation for a valid "Host" header. - // It's not exhaustive, only checks for valid characters. - if (isset($_SERVER['HTTP_HOST']) && preg_match('/^((\[[0-9a-f:]+\])|(\d{1,3}(\.\d{1,3}){3})|[a-z0-9\-\.]+)(:\d+)?$/i', $_SERVER['HTTP_HOST'])) + if (isset($_SERVER['SERVER_ADDR'])) { - $base_url = (is_https() ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'] + $base_url = (is_https() ? 'https' : 'http').'://'.$_SERVER['SERVER_ADDR'] .substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], basename($_SERVER['SCRIPT_FILENAME']))); } else @@ -123,10 +121,9 @@ class CI_Config { foreach ($this->_config_paths as $path) { - foreach (array($file, ENVIRONMENT.'/'.$file) as $location) + foreach (array($file, ENVIRONMENT.DIRECTORY_SEPARATOR.$file) as $location) { $file_path = $path.'config/'.$location.'.php'; - if (in_array($file_path, $this->is_loaded, TRUE)) { return TRUE; @@ -165,14 +162,13 @@ class CI_Config { $loaded = TRUE; log_message('debug', 'Config file loaded: '.$file_path); } - - if ($loaded === TRUE) - { - return TRUE; - } } - if ($fail_gracefully === TRUE) + if ($loaded === TRUE) + { + return TRUE; + } + elseif ($fail_gracefully === TRUE) { return FALSE; } @@ -240,7 +236,15 @@ class CI_Config { if (isset($protocol)) { - $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + // For protocol-relative links + if ($protocol === '') + { + $base_url = substr($base_url, strpos($base_url, '//')); + } + else + { + $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + } } if (empty($uri)) @@ -295,7 +299,15 @@ class CI_Config { if (isset($protocol)) { - $base_url = $protocol.substr($base_url, strpos($base_url, '://')); + // For protocol-relative links + if ($protocol === '') + { + $base_url = substr($base_url, strpos($base_url, '//')); + } + else + { + $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 fc25f57e6..d8f62c0fe 100644 --- a/system/core/Exceptions.php +++ b/system/core/Exceptions.php @@ -187,7 +187,7 @@ class CI_Exceptions { // -------------------------------------------------------------------- - public function show_exception(Exception $exception) + public function show_exception($exception) { $templates_path = config_item('error_views_path'); if (empty($templates_path)) diff --git a/system/core/Hooks.php b/system/core/Hooks.php index 08479b133..3b4fb2250 100644 --- a/system/core/Hooks.php +++ b/system/core/Hooks.php @@ -46,7 +46,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Libraries * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/encryption.html + * @link http://codeigniter.com/user_guide/general/hooks.html */ class CI_Hooks { diff --git a/system/core/Input.php b/system/core/Input.php index 72425c1c1..4e7a4e95e 100644 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -55,7 +55,7 @@ class CI_Input { * * @var string */ - public $ip_address = FALSE; + protected $ip_address = FALSE; /** * Allow GET array flag @@ -104,14 +104,28 @@ class CI_Input { protected $headers = array(); /** - * Input stream data + * Raw input stream data + * + * Holds a cache of php://input contents + * + * @var string + */ + protected $_raw_input_stream; + + /** + * Parsed input stream data * * Parsed from php://input at runtime * * @see CI_Input::input_stream() * @var array */ - protected $_input_stream = NULL; + protected $_input_stream; + + protected $security; + protected $uni; + + // -------------------------------------------------------------------- /** * Class constructor @@ -139,6 +153,12 @@ class CI_Input { // Sanitize global arrays $this->_sanitize_globals(); + // CSRF Protection check + if ($this->_enable_csrf === TRUE && ! is_cli()) + { + $this->security->csrf_verify(); + } + log_message('info', 'Input Class Initialized'); } @@ -313,7 +333,8 @@ class CI_Input { // so we'll need to check if we have already done that first. if ( ! is_array($this->_input_stream)) { - parse_str(file_get_contents('php://input'), $this->_input_stream); + // $this->raw_input_stream will trigger __get(). + parse_str($this->raw_input_stream, $this->_input_stream); is_array($this->_input_stream) OR $this->_input_stream = array(); } @@ -475,9 +496,9 @@ class CI_Input { ) ); - for ($i = 0; $i < 8; $i++) + for ($j = 0; $j < 8; $j++) { - $ip[$i] = intval($ip[$i], 16); + $ip[$j] = intval($ip[$j], 16); } $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b'; @@ -585,7 +606,7 @@ class CI_Input { { $_GET = array(); } - elseif (is_array($_GET) && count($_GET) > 0) + elseif (is_array($_GET)) { foreach ($_GET as $key => $val) { @@ -594,7 +615,7 @@ class CI_Input { } // Clean $_POST Data - if (is_array($_POST) && count($_POST) > 0) + if (is_array($_POST)) { foreach ($_POST as $key => $val) { @@ -603,7 +624,7 @@ class CI_Input { } // Clean $_COOKIE Data - if (is_array($_COOKIE) && count($_COOKIE) > 0) + if (is_array($_COOKIE)) { // Also get rid of specially treated cookies that might be set by a server // or silly application, that are of no use to a CI application anyway @@ -632,12 +653,6 @@ class CI_Input { // Sanitize PHP_SELF $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']); - // CSRF Protection check - if ($this->_enable_csrf === TRUE && ! is_cli()) - { - $this->security->csrf_verify(); - } - log_message('debug', 'Global POST, GET and COOKIE data sanitized'); } @@ -667,7 +682,7 @@ class CI_Input { /* We strip slashes if magic quotes is on to keep things consistent NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and - it will probably not exist in future versions at all. + it will probably not exist in future versions at all. */ if ( ! is_php('5.4') && get_magic_quotes_gpc()) { @@ -702,7 +717,7 @@ class CI_Input { * only named with alpha-numeric text and a few other items. * * @param string $str Input string - * @param string $fatal Whether to terminate script exection + * @param bool $fatal Whether to terminate script exection * or to return FALSE if an invalid * key is encountered * @return string|bool @@ -784,19 +799,27 @@ class CI_Input { */ public function get_request_header($index, $xss_clean = FALSE) { - if (empty($this->headers)) + static $headers; + + if ( ! isset($headers)) { - $this->request_headers(); + empty($this->headers) && $this->request_headers(); + foreach ($this->headers as $key => $value) + { + $headers[strtolower($key)] = $value; + } } - if ( ! isset($this->headers[$index])) + $index = strtolower($index); + + if ( ! isset($headers[$index])) { return NULL; } return ($xss_clean === TRUE) - ? $this->security->xss_clean($this->headers[$index]) - : $this->headers[$index]; + ? $this->security->xss_clean($headers[$index]) + : $headers[$index]; } // -------------------------------------------------------------------- @@ -821,7 +844,7 @@ class CI_Input { * Test to see if a request was made from the command line. * * @deprecated 3.0.0 Use is_cli() instead - * @return bool + * @return bool */ public function is_cli_request() { @@ -846,4 +869,27 @@ class CI_Input { : strtolower($this->server('REQUEST_METHOD')); } + // ------------------------------------------------------------------------ + + /** + * Magic __get() + * + * Allows read access to protected properties + * + * @param string $name + * @return mixed + */ + public function __get($name) + { + if ($name === 'raw_input_stream') + { + isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input'); + return $this->_raw_input_stream; + } + elseif ($name === 'ip_address') + { + return $this->ip_address; + } + } + } diff --git a/system/core/Loader.php b/system/core/Loader.php index b2eeb3b1d..18e4c5287 100644 --- a/system/core/Loader.php +++ b/system/core/Loader.php @@ -272,7 +272,7 @@ class CI_Loader { $CI =& get_instance(); if (isset($CI->$name)) { - show_error('The model name you are loading is the name of a resource that is already being used: '.$name); + throw new RuntimeException('The model name you are loading is the name of a resource that is already being used: '.$name); } if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE)) @@ -290,24 +290,38 @@ class CI_Loader { load_class('Model', 'core'); } - $model = ucfirst(strtolower($model)); - - foreach ($this->_ci_model_paths as $mod_path) + $model = ucfirst($model); + if ( ! class_exists($model)) { - if ( ! file_exists($mod_path.'models/'.$path.$model.'.php')) + foreach ($this->_ci_model_paths as $mod_path) { - continue; - } + if ( ! file_exists($mod_path.'models/'.$path.$model.'.php')) + { + continue; + } - require_once($mod_path.'models/'.$path.$model.'.php'); + require_once($mod_path.'models/'.$path.$model.'.php'); + if ( ! class_exists($model, FALSE)) + { + throw new RuntimeException($mod_path."models/".$path.$model.".php exists, but doesn't declare class ".$model); + } - $this->_ci_models[] = $name; - $CI->$name = new $model(); - return $this; + break; + } + + if ( ! class_exists($model, FALSE)) + { + throw new RuntimeException('Unable to locate the model you have specified: '.$model); + } + } + elseif ( ! is_subclass_of($model, 'CI_Model')) + { + throw new RuntimeException("Class ".$model." already exists and doesn't extend CI_Model"); } - // couldn't find the model - show_error('Unable to locate the model you have specified: '.$model); + $this->_ci_models[] = $name; + $CI->$name = new $model(); + return $this; } // -------------------------------------------------------------------- @@ -500,7 +514,7 @@ class CI_Loader { * * Clears the cached variables. * - * @return object + * @return CI_Loader */ public function clear_vars() { @@ -905,7 +919,7 @@ class CI_Loader { // If the PHP installation does not support short tags we'll // do a little string replacement, changing the short tags // to standard PHP echo statements. - if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE && function_usable('eval')) + if ( ! is_php('5.4') && ! ini_get('short_open_tag') && config_item('rewrite_short_tags') === TRUE) { echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace('<?=', '<?php echo ', file_get_contents($_ci_path)))); } @@ -1079,17 +1093,26 @@ class CI_Loader { log_message('debug', $library_name.' class already loaded. Second attempt ignored.'); return; } - elseif (file_exists(APPPATH.'libraries/'.$file_path.$library_name.'.php')) + + $paths = $this->_ci_library_paths; + array_pop($paths); // BASEPATH + array_pop($paths); // APPPATH (needs to be the first path checked) + array_unshift($paths, APPPATH); + + foreach ($paths as $path) { - // Override - include_once(APPPATH.'libraries/'.$file_path.$library_name.'.php'); - if (class_exists($prefix.$library_name, FALSE)) - { - return $this->_ci_init_library($library_name, $prefix, $params, $object_name); - } - else + if (file_exists($path = $path.'libraries/'.$file_path.$library_name.'.php')) { - log_message('debug', APPPATH.'libraries/'.$file_path.$library_name.'.php exists, but does not declare '.$prefix.$library_name); + // Override + include_once($path); + if (class_exists($prefix.$library_name, FALSE)) + { + return $this->_ci_init_library($library_name, $prefix, $params, $object_name); + } + else + { + log_message('debug', $path.' exists, but does not declare '.$prefix.$library_name); + } } } @@ -1097,16 +1120,20 @@ class CI_Loader { // Check for extensions $subclass = config_item('subclass_prefix').$library_name; - if (file_exists(APPPATH.'libraries/'.$file_path.$subclass.'.php')) + foreach ($paths as $path) { - include_once(APPPATH.'libraries/'.$file_path.$subclass.'.php'); - if (class_exists($subclass, FALSE)) - { - $prefix = config_item('subclass_prefix'); - } - else + if (file_exists($path = $path.'libraries/'.$file_path.$subclass.'.php')) { - log_message('debug', APPPATH.'libraries/'.$file_path.$subclass.'.php exists, but does not declare '.$subclass); + include_once($path); + if (class_exists($subclass, FALSE)) + { + $prefix = config_item('subclass_prefix'); + break; + } + else + { + log_message('debug', $path.' exists, but does not declare '.$subclass); + } } } @@ -1294,10 +1321,7 @@ class CI_Loader { } // Load all other libraries - foreach ($autoload['libraries'] as $item) - { - $this->library($item); - } + $this->library($autoload['libraries']); } // Autoload models diff --git a/system/core/Log.php b/system/core/Log.php index 833316273..e8cb401f5 100644 --- a/system/core/Log.php +++ b/system/core/Log.php @@ -70,13 +70,6 @@ class CI_Log { protected $_threshold = 1; /** - * Highest level of logging - * - * @var int - */ - protected $_threshold_max = 0; - - /** * Array of threshold levels to log * * @var array @@ -139,7 +132,7 @@ class CI_Log { } elseif (is_array($config['log_threshold'])) { - $this->_threshold = $this->_threshold_max; + $this->_threshold = 0; $this->_threshold_array = array_flip($config['log_threshold']); } diff --git a/system/core/Output.php b/system/core/Output.php index 02f66936c..76c1329d2 100644 --- a/system/core/Output.php +++ b/system/core/Output.php @@ -556,9 +556,16 @@ class CI_Output { .$CI->config->item('index_page') .$CI->uri->uri_string(); - if ($CI->config->item('cache_query_string') && ! empty($_SERVER['QUERY_STRING'])) + if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) { - $uri .= '?'.$_SERVER['QUERY_STRING']; + if (is_array($cache_query_string)) + { + $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); + } + else + { + $uri .= '?'.$_SERVER['QUERY_STRING']; + } } $cache_path .= md5($uri); @@ -646,9 +653,16 @@ class CI_Output { // Build the file path. The file name is an MD5 hash of the full URI $uri = $CFG->item('base_url').$CFG->item('index_page').$URI->uri_string; - if ($CFG->item('cache_query_string') && ! empty($_SERVER['QUERY_STRING'])) + if (($cache_query_string = $CFG->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) { - $uri .= '?'.$_SERVER['QUERY_STRING']; + if (is_array($cache_query_string)) + { + $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); + } + else + { + $uri .= '?'.$_SERVER['QUERY_STRING']; + } } $filepath = $cache_path.md5($uri); @@ -674,7 +688,7 @@ class CI_Output { $cache_info = unserialize($match[1]); $expire = $cache_info['expire']; - $last_modified = filemtime($cache_path); + $last_modified = filemtime($filepath); // Has the file expired? if ($_SERVER['REQUEST_TIME'] >= $expire && is_really_writable($cache_path)) @@ -729,13 +743,20 @@ class CI_Output { { $uri = $CI->uri->uri_string(); - if ($CI->config->item('cache_query_string') && ! empty($_SERVER['QUERY_STRING'])) + if (($cache_query_string = $CI->config->item('cache_query_string')) && ! empty($_SERVER['QUERY_STRING'])) { - $uri .= '?'.$_SERVER['QUERY_STRING']; + if (is_array($cache_query_string)) + { + $uri .= '?'.http_build_query(array_intersect_key($_GET, array_flip($cache_query_string))); + } + else + { + $uri .= '?'.$_SERVER['QUERY_STRING']; + } } } - $cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').$uri); + $cache_path .= md5($CI->config->item('base_url').$CI->config->item('index_page').ltrim($uri, '/')); if ( ! @unlink($cache_path)) { diff --git a/system/core/Router.php b/system/core/Router.php index eb3da2285..a84be1f1d 100644 --- a/system/core/Router.php +++ b/system/core/Router.php @@ -83,7 +83,7 @@ class CI_Router { * * @var string */ - public $directory = ''; + public $directory; /** * Default controller (and method if specific) @@ -105,7 +105,7 @@ class CI_Router { /** * Enable query strings flag * - * Determines wether to use GET parameters or segment URIs + * Determines whether to use GET parameters or segment URIs * * @var bool */ @@ -126,25 +126,16 @@ class CI_Router { $this->uri =& load_class('URI', 'core'); $this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE); + + // If a directory override is configured, it has to be set before any dynamic routing logic + is_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']); $this->_set_routing(); // Set any routing overrides that may exist in the main index file if (is_array($routing)) { - if (isset($routing['directory'])) - { - $this->set_directory($routing['directory']); - } - - if ( ! empty($routing['controller'])) - { - $this->set_class($routing['controller']); - } - - if ( ! empty($routing['function'])) - { - $this->set_method($routing['function']); - } + empty($routing['controller']) OR $this->set_class($routing['controller']); + empty($routing['function']) OR $this->set_method($routing['function']); } log_message('info', 'Router Class Initialized'); @@ -162,17 +153,44 @@ class CI_Router { */ protected function _set_routing() { + // Load the routes.php file. It would be great if we could + // skip this for enable_query_strings = TRUE, but then + // default_controller would be empty ... + if (file_exists(APPPATH.'config/routes.php')) + { + include(APPPATH.'config/routes.php'); + } + + if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php')) + { + include(APPPATH.'config/'.ENVIRONMENT.'/routes.php'); + } + + // Validate & get reserved routes + if (isset($route) && is_array($route)) + { + isset($route['default_controller']) && $this->default_controller = $route['default_controller']; + isset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes']; + unset($route['default_controller'], $route['translate_uri_dashes']); + $this->routes = $route; + } + // Are query strings enabled in the config file? Normally CI doesn't utilize query strings // since URI segments are more search-engine friendly, but they can optionally be used. // If this feature is enabled, we will gather the directory/class/method a little differently if ($this->enable_query_strings) { - $_d = $this->config->item('directory_trigger'); - $_d = isset($_GET[$_d]) ? trim($_GET[$_d], " \t\n\r\0\x0B/") : ''; - if ($_d !== '') + // If the directory is set at this time, it means an override exists, so skip the checks + if ( ! isset($this->directory)) { - $this->uri->filter_uri($_d); - $this->set_directory($_d); + $_d = $this->config->item('directory_trigger'); + $_d = isset($_GET[$_d]) ? trim($_GET[$_d], " \t\n\r\0\x0B/") : ''; + + if ($_d !== '') + { + $this->uri->filter_uri($_d); + $this->set_directory($_d); + } } $_c = trim($this->config->item('controller_trigger')); @@ -203,26 +221,6 @@ class CI_Router { return; } - // Load the routes.php file. - if (file_exists(APPPATH.'config/routes.php')) - { - include(APPPATH.'config/routes.php'); - } - - if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php')) - { - include(APPPATH.'config/'.ENVIRONMENT.'/routes.php'); - } - - // Validate & get reserved routes - if (isset($route) && is_array($route)) - { - isset($route['default_controller']) && $this->default_controller = $route['default_controller']; - isset($route['translate_uri_dashes']) && $this->translate_uri_dashes = $route['translate_uri_dashes']; - unset($route['default_controller'], $route['translate_uri_dashes']); - $this->routes = $route; - } - // Is there anything to parse? if ($this->uri->uri_string !== '') { @@ -333,6 +331,8 @@ class CI_Router { protected function _validate_request($segments) { $c = count($segments); + $directory_override = isset($this->directory); + // Loop through our segments and return as soon as a controller // is found or when such a directory doesn't exist while ($c-- > 0) @@ -340,7 +340,10 @@ class CI_Router { $test = $this->directory .ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]); - if ( ! file_exists(APPPATH.'controllers/'.$test.'.php') && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0])) + if ( ! file_exists(APPPATH.'controllers/'.$test.'.php') + && $directory_override === FALSE + && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]) + ) { $this->set_directory(array_shift($segments), TRUE); continue; @@ -371,29 +374,13 @@ class CI_Router { // Get HTTP verb $http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli'; - // Is there a literal match? If so we're done - if (isset($this->routes[$uri])) - { - // Check default routes format - if (is_string($this->routes[$uri])) - { - $this->_set_request(explode('/', $this->routes[$uri])); - return; - } - // Is there a matching http verb? - elseif (is_array($this->routes[$uri]) && isset($this->routes[$uri][$http_verb])) - { - $this->_set_request(explode('/', $this->routes[$uri][$http_verb])); - return; - } - } - // Loop through the route array looking for wildcards foreach ($this->routes as $key => $val) { - // Check if route format is using http verb + // Check if route format is using HTTP verbs if (is_array($val)) { + $val = array_change_key_case($val, CASE_LOWER); if (isset($val[$http_verb])) { $val = $val[$http_verb]; @@ -493,7 +480,7 @@ class CI_Router { * Set directory name * * @param string $dir Directory name - * @param bool $appent Whether we're appending rather then setting the full value + * @param bool $append Whether we're appending rather than setting the full value * @return void */ public function set_directory($dir, $append = FALSE) diff --git a/system/core/Security.php b/system/core/Security.php index 7c18c7406..36dea4cf2 100644 --- a/system/core/Security.php +++ b/system/core/Security.php @@ -275,7 +275,7 @@ class CI_Security { $secure_cookie, config_item('cookie_httponly') ); - log_message('info', 'CRSF cookie sent'); + log_message('info', 'CSRF cookie sent'); return $this; } @@ -436,7 +436,7 @@ class CI_Security { $words = array( 'javascript', 'expression', 'vbscript', 'jscript', 'wscript', 'vbs', 'script', 'base64', 'applet', 'alert', 'document', - 'write', 'cookie', 'window', 'confirm', 'prompt' + 'write', 'cookie', 'window', 'confirm', 'prompt', 'eval' ); foreach ($words as $word) @@ -480,12 +480,8 @@ class CI_Security { } } while ($original !== $str); - unset($original); - // Remove evil attributes such as style, onclick and xmlns - $str = $this->_remove_evil_attributes($str, $is_image); - /* * Sanitize naughty HTML elements * @@ -495,8 +491,29 @@ class CI_Security { * So this: <blink> * Becomes: <blink> */ - $naughty = 'alert|prompt|confirm|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|button|select|isindex|layer|link|meta|keygen|object|plaintext|style|script|textarea|title|math|video|svg|xml|xss'; - $str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is', array($this, '_sanitize_naughty_html'), $str); + $pattern = '#' + .'<((?<slash>/*\s*)(?<tagName>[a-z0-9]+)(?=[^a-z0-9]|$)' // tag start and name, followed by a non-tag character + .'[^\s\042\047a-z0-9>/=]*' // a valid attribute character immediately after the tag would count as a separator + // optional attributes + .'(?<attributes>(?:[\s\042\047/=]*' // non-attribute characters, excluding > (tag close) for obvious reasons + .'[^\s\042\047>/=]+' // attribute characters + // optional attribute-value + .'(?:\s*=' // attribute-value separator + .'(?:[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*))' // single, double or non-quoted value + .')?' // end optional attribute-value group + .')*)' // end optional attributes group + .'[^>]*)(?<closeTag>\>)?#isS'; + + // Note: It would be nice to optimize this for speed, BUT + // only matching the naughty elements here results in + // false positives and in turn - vulnerabilities! + do + { + $old_str = $str; + $str = preg_replace_callback($pattern, array($this, '_sanitize_naughty_html'), $str); + } + while ($old_str !== $str); + unset($old_str); /* * Sanitize naughty scripting elements @@ -510,9 +527,11 @@ class CI_Security { * For example: eval('some code') * Becomes: eval('some code') */ - $str = preg_replace('#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', - '\\1\\2(\\3)', - $str); + $str = preg_replace( + '#(alert|prompt|confirm|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', + '\\1\\2(\\3)', + $str + ); // Final clean up // This adds a bit of extra precaution in case @@ -639,7 +658,7 @@ class CI_Security { $str_compare = $str; // Decode standard entities, avoiding false positives - if ($c = preg_match_all('/&[a-z]{2,}(?![a-z;])/i', $str, $matches)) + if (preg_match_all('/&[a-z]{2,}(?![a-z;])/i', $str, $matches)) { if ( ! isset($_entities)) { @@ -656,7 +675,7 @@ class CI_Security { { $_entities[':'] = ':'; $_entities['('] = '('; - $_entities[')'] = '&rpar'; + $_entities[')'] = ')'; $_entities["\n"] = '&newline;'; $_entities["\t"] = '&tab;'; } @@ -664,11 +683,11 @@ class CI_Security { $replace = array(); $matches = array_unique(array_map('strtolower', $matches[0])); - for ($i = 0; $i < $c; $i++) + foreach ($matches as &$match) { - if (($char = array_search($matches[$i].';', $_entities, TRUE)) !== FALSE) + if (($char = array_search($match.';', $_entities, TRUE)) !== FALSE) { - $replace[$matches[$i]] = $char; + $replace[$match] = $char; } } @@ -750,86 +769,92 @@ class CI_Security { // -------------------------------------------------------------------- /** - * Remove Evil HTML Attributes (like event handlers and style) - * - * It removes the evil attribute and either: - * - * - Everything up until a space. For example, everything between the pipes: - * - * <code> - * <a |style=document.write('hello');alert('world');| class=link> - * </code> - * - * - Everything inside the quotes. For example, everything between the pipes: + * Sanitize Naughty HTML * - * <code> - * <a |style="document.write('hello'); alert('world');"| class="link"> - * </code> + * Callback method for xss_clean() to remove naughty HTML elements. * - * @param string $str The string to check - * @param bool $is_image Whether the input is an image - * @return string The string with the evil attributes removed + * @used-by CI_Security::xss_clean() + * @param array $matches + * @return string */ - protected function _remove_evil_attributes($str, $is_image) + protected function _sanitize_naughty_html($matches) { - $evil_attributes = array('on\w*', 'style', 'xmlns', 'formaction', 'form', 'xlink:href'); + static $naughty_tags = array( + 'alert', 'prompt', 'confirm', 'applet', 'audio', 'basefont', 'base', 'behavior', 'bgsound', + 'blink', 'body', 'embed', 'expression', 'form', 'frameset', 'frame', 'head', 'html', 'ilayer', + 'iframe', 'input', 'button', 'select', 'isindex', 'layer', 'link', 'meta', 'keygen', 'object', + 'plaintext', 'style', 'script', 'textarea', 'title', 'math', 'video', 'svg', 'xml', 'xss' + ); - if ($is_image === TRUE) + static $evil_attributes = array( + 'on\w+', 'style', 'xmlns', 'formaction', 'form', 'xlink:href', 'FSCommand', 'seekSegmentTime' + ); + + // First, escape unclosed tags + if (empty($matches['closeTag'])) { - /* - * Adobe Photoshop puts XML metadata into JFIF images, - * including namespacing, so we have to allow this for images. - */ - unset($evil_attributes[array_search('xmlns', $evil_attributes)]); + return '<'.$matches[1]; } + // Is the element that we caught naughty? If so, escape it + elseif (in_array(strtolower($matches['tagName']), $naughty_tags, TRUE)) + { + return '<'.$matches[1].'>'; + } + // For other tags, see if their attributes are "evil" and strip those + elseif (isset($matches['attributes'])) + { + // We'll store the already fitlered attributes here + $attributes = array(); - do { - $count = 0; - $attribs = array(); + // Attribute-catching pattern + $attributes_pattern = '#' + .'(?<name>[^\s\042\047>/=]+)' // attribute characters + // optional attribute-value + .'(?:\s*=(?<value>[^\s\042\047=><`]+|\s*\042[^\042]*\042|\s*\047[^\047]*\047|\s*(?U:[^\s\042\047=><`]*)))' // attribute-value separator + .'#i'; - // find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes) - preg_match_all('/(?<!\w)('.implode('|', $evil_attributes).')\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is', $str, $matches, PREG_SET_ORDER); + // Blacklist pattern for evil attribute names + $is_evil_pattern = '#^('.implode('|', $evil_attributes).')$#i'; - foreach ($matches as $attr) + // Each iteration filters a single attribute + do { - $attribs[] = preg_quote($attr[0], '/'); - } + // Strip any non-alpha characters that may preceed an attribute. + // Browsers often parse these incorrectly and that has been a + // of numerous XSS issues we've had. + $matches['attributes'] = preg_replace('#^[^a-z]+#i', '', $matches['attributes']); - // find occurrences of illegal attribute strings without quotes - preg_match_all('/(?<!\w)('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER); + if ( ! preg_match($attributes_pattern, $matches['attributes'], $attribute, PREG_OFFSET_CAPTURE)) + { + // No (valid) attribute found? Discard everything else inside the tag + break; + } - foreach ($matches as $attr) - { - $attribs[] = preg_quote($attr[0], '/'); - } + if ( + // Is it indeed an "evil" attribute? + preg_match($is_evil_pattern, $attribute['name'][0]) + // Or does it have an equals sign, but no value and not quoted? Strip that too! + OR (trim($attribute['value'][0]) === '') + ) + { + $attributes[] = 'xss=removed'; + } + else + { + $attributes[] = $attribute[0][0]; + } - // replace illegal attribute strings that are inside an html tag - if (count($attribs) > 0) - { - $str = preg_replace('/(<?)(\/?[^><]+?)([^A-Za-z<>\-])(.*?)('.implode('|', $attribs).')(.*?)([\s><]?)([><]*)/i', '$1$2 $4$6$7$8', $str, -1, $count); + $matches['attributes'] = substr($matches['attributes'], $attribute[0][1] + strlen($attribute[0][0])); } + while ($matches['attributes'] !== ''); + + $attributes = empty($attributes) + ? '' + : ' '.implode(' ', $attributes); + return '<'.$matches['slash'].$matches['tagName'].$attributes.'>'; } - while ($count); - return $str; - } - - // -------------------------------------------------------------------- - - /** - * Sanitize Naughty HTML - * - * Callback method for xss_clean() to remove naughty HTML elements. - * - * @used-by CI_Security::xss_clean() - * @param array $matches - * @return string - */ - protected function _sanitize_naughty_html($matches) - { - return '<'.$matches[1].$matches[2].$matches[3] // encode opening brace - // encode captured opening or closing brace to prevent recursive vectors: - .str_replace(array('>', '<'), array('>', '<'), $matches[4]); + return $matches[0]; } // -------------------------------------------------------------------- @@ -849,12 +874,15 @@ class CI_Security { */ protected function _js_link_removal($match) { - return str_replace($match[1], - preg_replace('#href=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si', - '', - $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])) - ), - $match[0]); + return str_replace( + $match[1], + preg_replace( + '#href=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si', + '', + $this->_filter_attributes($match[1]) + ), + $match[0] + ); } // -------------------------------------------------------------------- @@ -874,12 +902,15 @@ class CI_Security { */ protected function _js_img_removal($match) { - return str_replace($match[1], - preg_replace('#src=.*?(?:(?:alert|prompt|confirm)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si', - '', - $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])) - ), - $match[0]); + return str_replace( + $match[1], + preg_replace( + '#src=.*?(?:(?:alert|prompt|confirm|eval)(?:\(|&\#40;)|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si', + '', + $this->_filter_attributes($match[1]) + ), + $match[0] + ); } // -------------------------------------------------------------------- diff --git a/system/core/URI.php b/system/core/URI.php index 9bc34ace7..5b658f679 100644 --- a/system/core/URI.php +++ b/system/core/URI.php @@ -107,37 +107,34 @@ class CI_URI { $this->_permitted_uri_chars = $this->config->item('permitted_uri_chars'); // If it's a CLI request, ignore the configuration - if (is_cli() OR ($protocol = strtoupper($this->config->item('uri_protocol'))) === 'CLI') + if (is_cli()) { - $this->_set_uri_string($this->_parse_argv()); + $uri = $this->_parse_argv(); } - elseif ($protocol === 'AUTO') + else { - // Is there a PATH_INFO variable? This should be the easiest solution. - if (isset($_SERVER['PATH_INFO'])) - { - $this->_set_uri_string($_SERVER['PATH_INFO']); - } - // No PATH_INFO? Let's try REQUST_URI or QUERY_STRING then - elseif (($uri = $this->_parse_request_uri()) !== '' OR ($uri = $this->_parse_query_string()) !== '') - { - $this->_set_uri_string($uri); - } - // As a last ditch effor, let's try using the $_GET array - elseif (is_array($_GET) && count($_GET) === 1 && trim(key($_GET), '/') !== '') + $protocol = $this->config->item('uri_protocol'); + empty($protocol) && $protocol = 'REQUEST_URI'; + + switch ($protocol) { - $this->_set_uri_string(key($_GET)); + case 'AUTO': // For BC purposes only + case 'REQUEST_URI': + $uri = $this->_parse_request_uri(); + break; + case 'QUERY_STRING': + $uri = $this->_parse_query_string(); + break; + case 'PATH_INFO': + default: + $uri = isset($_SERVER[$protocol]) + ? $_SERVER[$protocol] + : $this->_parse_request_uri(); + break; } } - elseif (method_exists($this, ($method = '_parse_'.strtolower($protocol)))) - { - $this->_set_uri_string($this->$method()); - } - else - { - $uri = isset($_SERVER[$protocol]) ? $_SERVER[$protocol] : @getenv($protocol); - $this->_set_uri_string($uri); - } + + $this->_set_uri_string($uri); } log_message('info', 'URI Class Initialized'); @@ -204,17 +201,22 @@ class CI_URI { return ''; } - $uri = parse_url($_SERVER['REQUEST_URI']); + // parse_url() returns false if no host is present, but the path or query string + // contains a colon followed by a number + $uri = parse_url('http://dummy'.$_SERVER['REQUEST_URI']); $query = isset($uri['query']) ? $uri['query'] : ''; - $uri = isset($uri['path']) ? rawurldecode($uri['path']) : ''; + $uri = isset($uri['path']) ? $uri['path'] : ''; - if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) - { - $uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME'])); - } - elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) + if (isset($_SERVER['SCRIPT_NAME'][0])) { - $uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); + if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) + { + $uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME'])); + } + elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) + { + $uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); + } } // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct @@ -222,7 +224,7 @@ class CI_URI { if (trim($uri, '/') === '' && strncmp($query, '/', 1) === 0) { $query = explode('?', $query, 2); - $uri = rawurldecode($query[0]); + $uri = $query[0]; $_SERVER['QUERY_STRING'] = isset($query[1]) ? $query[1] : ''; } else @@ -262,7 +264,7 @@ class CI_URI { { $uri = explode('?', $uri, 2); $_SERVER['QUERY_STRING'] = isset($uri[1]) ? $uri[1] : ''; - $uri = rawurldecode($uri[0]); + $uri = $uri[0]; } parse_str($_SERVER['QUERY_STRING'], $_GET); diff --git a/system/core/compat/hash.php b/system/core/compat/hash.php index 477535dca..15954559c 100644 --- a/system/core/compat/hash.php +++ b/system/core/compat/hash.php @@ -174,9 +174,56 @@ if ( ! function_exists('hash_pbkdf2')) } $hash_length = strlen(hash($algo, NULL, TRUE)); - if (empty($length)) + empty($length) && $length = $hash_length; + + // Pre-hash password inputs longer than the algorithm's block size + // (i.e. prepare HMAC key) to mitigate potential DoS attacks. + static $block_sizes; + empty($block_sizes) && $block_sizes = array( + 'gost' => 32, + 'haval128,3' => 128, + 'haval160,3' => 128, + 'haval192,3' => 128, + 'haval224,3' => 128, + 'haval256,3' => 128, + 'haval128,4' => 128, + 'haval160,4' => 128, + 'haval192,4' => 128, + 'haval224,4' => 128, + 'haval256,4' => 128, + 'haval128,5' => 128, + 'haval160,5' => 128, + 'haval192,5' => 128, + 'haval224,5' => 128, + 'haval256,5' => 128, + 'md2' => 16, + 'md4' => 64, + 'md5' => 64, + 'ripemd128' => 64, + 'ripemd160' => 64, + 'ripemd256' => 64, + 'ripemd320' => 64, + 'salsa10' => 64, + 'salsa20' => 64, + 'sha1' => 64, + 'sha224' => 64, + 'sha256' => 64, + 'sha384' => 128, + 'sha512' => 128, + 'snefru' => 32, + 'snefru256' => 32, + 'tiger128,3' => 64, + 'tiger160,3' => 64, + 'tiger192,3' => 64, + 'tiger128,4' => 64, + 'tiger160,4' => 64, + 'tiger192,4' => 64, + 'whirlpool' => 64 + ); + + if (isset($block_sizes[$algo]) && strlen($password) > $block_sizes[$algo]) { - $length = $hash_length; + $password = hash($algo, $password, TRUE); } $hash = ''; diff --git a/system/core/compat/index.html b/system/core/compat/index.html index c942a79ce..b702fbc39 100644 --- a/system/core/compat/index.html +++ b/system/core/compat/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/core/compat/mbstring.php b/system/core/compat/mbstring.php index ddb2bae47..e335c85f7 100644 --- a/system/core/compat/mbstring.php +++ b/system/core/compat/mbstring.php @@ -92,7 +92,7 @@ if ( ! function_exists('mb_strpos')) * WARNING: This function WILL fall-back to strpos() * if iconv is not available! * - * @link http://php.net/mb_strpos() + * @link http://php.net/mb_strpos * @param string $haystack * @param string $needle * @param int $offset diff --git a/system/core/index.html b/system/core/index.html index c942a79ce..b702fbc39 100644 --- a/system/core/index.html +++ b/system/core/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/DB.php b/system/database/DB.php index 8ea7ca6fa..23de414b5 100644 --- a/system/database/DB.php +++ b/system/database/DB.php @@ -82,7 +82,7 @@ function &DB($params = '', $query_builder_override = NULL) } } - if ( ! isset($db) OR count($db) === 0) + if (empty($db)) { show_error('No database connection settings were found in the database config file.'); } @@ -213,10 +213,6 @@ function &DB($params = '', $query_builder_override = NULL) } } - if ($DB->autoinit === TRUE) - { - $DB->initialize(); - } - + $DB->initialize(); return $DB; } diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php index bbe65b410..0ea679432 100644 --- a/system/database/DB_driver.php +++ b/system/database/DB_driver.php @@ -124,15 +124,6 @@ abstract class CI_DB_driver { public $dbcollat = 'utf8_general_ci'; /** - * Auto-init flag - * - * Whether to automatically initialize the DB connection. - * - * @var bool - */ - public $autoinit = TRUE; - - /** * Encryption flag/data * * @var mixed @@ -460,7 +451,7 @@ abstract class CI_DB_driver { * * This is just a dummy method that all drivers will override. * - * @return mixed + * @return mixed */ public function db_connect() { @@ -490,7 +481,7 @@ abstract class CI_DB_driver { * This is just a dummy method to allow drivers without such * functionality to not declare it, while others will override it. * - * @return void + * @return void */ public function reconnect() { @@ -504,7 +495,7 @@ abstract class CI_DB_driver { * This is just a dummy method to allow drivers without such * functionality to not declare it, while others will override it. * - * @return bool + * @return bool */ public function db_select() { @@ -656,7 +647,10 @@ abstract class CI_DB_driver { } // This will trigger a rollback if transactions are being used - $this->_trans_status = FALSE; + if ($this->_trans_depth !== 0) + { + $this->_trans_status = FALSE; + } // Grab the error now, as we might run some additional queries before displaying the error $error = $this->error(); @@ -674,7 +668,13 @@ abstract class CI_DB_driver { { do { + $trans_depth = $this->_trans_depth; $this->trans_complete(); + if ($trans_depth === $this->_trans_depth) + { + log_message('error', 'Database: Failure during an automated transaction commit/rollback!'); + break; + } } while ($this->_trans_depth !== 0); } @@ -797,10 +797,13 @@ abstract class CI_DB_driver { /** * Enable/disable Transaction Strict Mode + * * When strict mode is enabled, if you are running multiple groups of - * transactions, if one group fails all groups will be rolled back. - * If strict mode is disabled, each group is treated autonomously, meaning - * a failure of one group will not affect any others + * transactions, if one group fails all subsequent groups will be + * rolled back. + * + * If strict mode is disabled, each group is treated autonomously, + * meaning a failure of one group will not affect any others * * @param bool $mode = TRUE * @return void @@ -816,24 +819,16 @@ abstract class CI_DB_driver { * Start Transaction * * @param bool $test_mode = FALSE - * @return void + * @return bool */ public function trans_start($test_mode = FALSE) { if ( ! $this->trans_enabled) { - return; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - $this->_trans_depth += 1; - return; + return FALSE; } - $this->trans_begin($test_mode); - $this->_trans_depth += 1; + return $this->trans_begin($test_mode); } // -------------------------------------------------------------------- @@ -850,25 +845,14 @@ abstract class CI_DB_driver { return FALSE; } - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 1) - { - $this->_trans_depth -= 1; - return TRUE; - } - else - { - $this->_trans_depth = 0; - } - // The query() function will set this flag to FALSE in the event that a query failed if ($this->_trans_status === FALSE OR $this->_trans_failure === TRUE) { $this->trans_rollback(); // If we are NOT running in strict mode, we will reset - // the _trans_status flag so that subsequent groups of transactions - // will be permitted. + // the _trans_status flag so that subsequent groups of + // transactions will be permitted. if ($this->trans_strict === FALSE) { $this->_trans_status = TRUE; @@ -878,8 +862,7 @@ abstract class CI_DB_driver { return FALSE; } - $this->trans_commit(); - return TRUE; + return $this->trans_commit(); } // -------------------------------------------------------------------- @@ -897,6 +880,87 @@ abstract class CI_DB_driver { // -------------------------------------------------------------------- /** + * Begin Transaction + * + * @param bool $test_mode + * @return bool + */ + public function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return FALSE; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 0) + { + $this->_trans_depth++; + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE); + + if ($this->_trans_begin()) + { + $this->_trans_depth++; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @return bool + */ + public function trans_commit() + { + if ( ! $this->trans_enabled OR $this->_trans_depth === 0) + { + return FALSE; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 1 OR $this->_trans_commit()) + { + $this->_trans_depth--; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @return bool + */ + public function trans_rollback() + { + if ( ! $this->trans_enabled OR $this->_trans_depth === 0) + { + return FALSE; + } + // When transactions are nested we only begin/commit/rollback the outermost ones + elseif ($this->_trans_depth > 1 OR $this->_trans_rollback()) + { + $this->_trans_depth--; + return TRUE; + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** * Compile Bindings * * @param string the sql statement @@ -1227,7 +1291,7 @@ abstract class CI_DB_driver { /** * Fetch Field Names * - * @param string the table name + * @param string $table Table name * @return array */ public function list_fields($table) @@ -1486,18 +1550,18 @@ abstract class CI_DB_driver { ? '\s+'.preg_quote(trim(sprintf($this->_like_escape_str, $this->_like_escape_chr)), '/') : ''; $_operators = array( - '\s*(?:<|>|!)?=\s*', // =, <=, >=, != - '\s*<>?\s*', // <, <> - '\s*>\s*', // > - '\s+IS NULL', // IS NULL - '\s+IS NOT NULL', // IS NOT NULL - '\s+EXISTS\s*\([^\)]+\)', // EXISTS(sql) - '\s+NOT EXISTS\s*\([^\)]+\)', // NOT EXISTS(sql) - '\s+BETWEEN\s+\S+\s+AND\s+\S+', // BETWEEN value AND value - '\s+IN\s*\([^\)]+\)', // IN(list) - '\s+NOT IN\s*\([^\)]+\)', // NOT IN (list) - '\s+LIKE\s+\S+'.$_les, // LIKE 'expr'[ ESCAPE '%s'] - '\s+NOT LIKE\s+\S+'.$_les // NOT LIKE 'expr'[ ESCAPE '%s'] + '\s*(?:<|>|!)?=\s*', // =, <=, >=, != + '\s*<>?\s*', // <, <> + '\s*>\s*', // > + '\s+IS NULL', // IS NULL + '\s+IS NOT NULL', // IS NOT NULL + '\s+EXISTS\s*\([^\)]+\)', // EXISTS(sql) + '\s+NOT EXISTS\s*\([^\)]+\)', // NOT EXISTS(sql) + '\s+BETWEEN\s+', // BETWEEN value AND value + '\s+IN\s*\([^\)]+\)', // IN(list) + '\s+NOT IN\s*\([^\)]+\)', // NOT IN (list) + '\s+LIKE\s+\S.*('.$_les.')?', // LIKE 'expr'[ ESCAPE '%s'] + '\s+NOT LIKE\s+\S.*('.$_les.')?' // NOT LIKE 'expr'[ ESCAPE '%s'] ); } @@ -1760,13 +1824,13 @@ abstract class CI_DB_driver { // // Added exception for single quotes as well, we don't want to alter // literal strings. -- Narf - if (strpos($item, '(') !== FALSE OR strpos($item, "'") !== FALSE) + if (strcspn($item, "()'") !== strlen($item)) { return $item; } // Convert tabs or multiple spaces into single spaces - $item = preg_replace('/\s+/', ' ', $item); + $item = preg_replace('/\s+/', ' ', trim($item)); // If the item has an alias declaration we remove it and set it aside. // Note: strripos() is used in order to support spaces in table names @@ -1794,12 +1858,15 @@ abstract class CI_DB_driver { // with an alias. While we're at it, we will escape the components if (strpos($item, '.') !== FALSE) { - $parts = explode('.', $item); + $parts = explode('.', $item); // Does the first segment of the exploded item match // one of the aliases previously identified? If so, // we have nothing more to do other than escape the item - if (in_array($parts[0], $this->qb_aliased_tables)) + // + // NOTE: The ! empty() condition prevents this method + // from breaking when QB isn't enabled. + if ( ! empty($this->qb_aliased_tables) && in_array($parts[0], $this->qb_aliased_tables)) { if ($protect_identifiers === TRUE) { diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php index f6ee2a63a..f9cf76a14 100644 --- a/system/database/DB_forge.php +++ b/system/database/DB_forge.php @@ -143,7 +143,7 @@ abstract class CI_DB_forge { protected $_unsigned = TRUE; /** - * NULL value representatin in CREATE/ALTER TABLE statements + * NULL value representation in CREATE/ALTER TABLE statements * * @var string */ @@ -239,6 +239,12 @@ abstract class CI_DB_forge { */ public function add_key($key, $primary = FALSE) { + // DO NOT change this! This condition is only applicable + // for PRIMARY keys because you can only have one such, + // and therefore all fields you add to it will be included + // in the same, composite PRIMARY KEY. + // + // It's not the same for regular indexes. if ($primary === TRUE && is_array($key)) { foreach ($key as $one) @@ -453,12 +459,7 @@ abstract class CI_DB_forge { return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE; } - $query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists); - if ($query === FALSE) - { - return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE; - } - elseif ($query === TRUE) + if (($query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists)) === TRUE) { return TRUE; } diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php index 79cbfb3ad..cf1100d27 100644 --- a/system/database/DB_query_builder.php +++ b/system/database/DB_query_builder.php @@ -794,13 +794,23 @@ abstract class CI_DB_query_builder extends CI_DB_driver { $not = ($not) ? ' NOT' : ''; - $where_in = array(); - foreach ($values as $value) + if ($escape === TRUE) { - $where_in[] = $this->escape($value); + $where_in = array(); + foreach ($values as $value) + { + $where_in[] = $this->escape($value); + } } + else + { + $where_in = array_values($values); + } + + $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) + ? $this->_group_get_type('') + : $this->_group_get_type($type); - $prefix = (count($this->qb_where) === 0) ? $this->_group_get_type('') : $this->_group_get_type($type); $where_in = array( 'condition' => $prefix.$key.$not.' IN('.implode(', ', $where_in).')', 'escape' => $escape @@ -918,13 +928,18 @@ abstract class CI_DB_query_builder extends CI_DB_driver { } is_bool($escape) OR $escape = $this->_protect_identifiers; + // lowercase $side in case somebody writes e.g. 'BEFORE' instead of 'before' (doh) + $side = strtolower($side); foreach ($field as $k => $v) { $prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) ? $this->_group_get_type('') : $this->_group_get_type($type); - $v = $this->escape_like_str($v); + if ($escape === TRUE) + { + $v = $this->escape_like_str($v); + } if ($side === 'none') { @@ -944,7 +959,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { } // some platforms require an escape sequence definition for LIKE wildcards - if ($this->_like_escape_str !== '') + if ($escape === TRUE && $this->_like_escape_str !== '') { $like_statement .= sprintf($this->_like_escape_str, $this->_like_escape_chr); } @@ -1292,7 +1307,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { * Compiles a SELECT query string and returns the sql. * * @param string the table name to select from (optional) - * @param bool TRUE: resets QB values; FALSE: leave QB vaules alone + * @param bool TRUE: resets QB values; FALSE: leave QB values alone * @return string */ public function get_compiled_select($table = '', $reset = TRUE) @@ -1353,9 +1368,10 @@ abstract class CI_DB_query_builder extends CI_DB_driver { * returned by an Query Builder query. * * @param string + * @param bool the reset clause * @return int */ - public function count_all_results($table = '') + public function count_all_results($table = '', $reset = TRUE) { if ($table !== '') { @@ -1366,7 +1382,11 @@ abstract class CI_DB_query_builder extends CI_DB_driver { $result = ($this->qb_distinct === TRUE) ? $this->query($this->_count_string.$this->protect_identifiers('numrows')."\nFROM (\n".$this->_compile_select()."\n) CI_count_all_results") : $this->query($this->_compile_select($this->_count_string.$this->protect_identifiers('numrows'))); - $this->_reset_select(); + + if ($reset === TRUE) + { + $this->_reset_select(); + } if ($result->num_rows() === 0) { @@ -1729,7 +1749,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { return FALSE; } - $sql = $this->_update($this->protect_identifiers($this->qb_from[0], TRUE, NULL, FALSE), $this->qb_set); + $sql = $this->_update($this->qb_from[0], $this->qb_set); if ($reset === TRUE) { @@ -1777,7 +1797,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { $this->limit($limit); } - $sql = $this->_update($this->protect_identifiers($this->qb_from[0], TRUE, NULL, FALSE), $this->qb_set); + $sql = $this->_update($this->qb_from[0], $this->qb_set); $this->_reset_write(); return $this->query($sql); } @@ -1794,7 +1814,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { * @param string the table to update data on * @return bool */ - protected function _validate_update($table = '') + protected function _validate_update($table) { if (count($this->qb_set) === 0) { @@ -1803,7 +1823,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { if ($table !== '') { - $this->qb_from[0] = $table; + $this->qb_from = array($this->protect_identifiers($table, TRUE, NULL, FALSE)); } elseif ( ! isset($this->qb_from[0])) { @@ -2085,10 +2105,13 @@ abstract class CI_DB_query_builder extends CI_DB_driver { } elseif (is_array($table)) { + empty($where) && $reset_data = FALSE; + foreach ($table as $single_table) { $this->delete($single_table, $where, $limit, $reset_data); } + return; } else @@ -2248,7 +2271,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { else { // Cycle through the "select" portion of the query and prep each column name. - // The reason we protect identifiers here rather then in the select() function + // The reason we protect identifiers here rather than in the select() function // is because until the user calls the from() function we don't know if there are aliases foreach ($this->qb_select as $key => $val) { @@ -2319,7 +2342,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { // Split multiple conditions $conditions = preg_split( - '/(\s*AND\s+|\s*OR\s+)/i', + '/((?:^|\s+)AND\s+|(?:^|\s+)OR\s+)/i', $this->{$qb_key}[$i]['condition'], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY diff --git a/system/database/DB_utility.php b/system/database/DB_utility.php index 57356ac53..b51893e18 100644 --- a/system/database/DB_utility.php +++ b/system/database/DB_utility.php @@ -249,16 +249,17 @@ abstract class CI_DB_utility { $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim; } - $out = substr(rtrim($out), 0, -strlen($delim)).$newline; + $out = substr($out, 0, -strlen($delim)).$newline; // Next blast through the result array and build out the rows while ($row = $query->unbuffered_row('array')) { + $line = array(); foreach ($row as $item) { - $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure.$delim; + $line[] = $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure; } - $out = substr(rtrim($out), 0, -strlen($delim)).$newline; + $out .= implode($delim, $line).$newline; } return $out; diff --git a/system/database/drivers/cubrid/cubrid_driver.php b/system/database/drivers/cubrid/cubrid_driver.php index 402117978..65f4adb3f 100644 --- a/system/database/drivers/cubrid/cubrid_driver.php +++ b/system/database/drivers/cubrid/cubrid_driver.php @@ -163,10 +163,6 @@ class CI_DB_cubrid_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } return ( ! $this->conn_id OR ($version = cubrid_get_server_info($this->conn_id)) === FALSE) ? FALSE @@ -191,25 +187,17 @@ class CI_DB_cubrid_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if (($autocommit = cubrid_get_autocommit($this->conn_id)) === NULL) { - return TRUE; + return FALSE; } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - - if (cubrid_get_autocommit($this->conn_id)) + elseif ($autocommit === TRUE) { - cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_FALSE); + return cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_FALSE); } return TRUE; @@ -222,19 +210,16 @@ class CI_DB_cubrid_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if ( ! cubrid_commit($this->conn_id)) { - return TRUE; + return FALSE; } - cubrid_commit($this->conn_id); - if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id)) { - cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); + return cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); } return TRUE; @@ -247,16 +232,13 @@ class CI_DB_cubrid_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if ( ! cubrid_rollback($this->conn_id)) { - return TRUE; + return FALSE; } - cubrid_rollback($this->conn_id); - if ($this->auto_commit && ! cubrid_get_autocommit($this->conn_id)) { cubrid_set_autocommit($this->conn_id, CUBRID_AUTOCOMMIT_TRUE); diff --git a/system/database/drivers/cubrid/index.html b/system/database/drivers/cubrid/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/cubrid/index.html +++ b/system/database/drivers/cubrid/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/ibase/ibase_driver.php b/system/database/drivers/ibase/ibase_driver.php index 529c320cd..82550d51b 100644 --- a/system/database/drivers/ibase/ibase_driver.php +++ b/system/database/drivers/ibase/ibase_driver.php @@ -134,24 +134,16 @@ class CI_DB_ibase_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if (($trans_handle = ibase_trans($this->conn_id)) === FALSE) { - return TRUE; + return FALSE; } - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - - $this->_ibase_trans = ibase_trans($this->conn_id); - + $this->_ibase_trans = $trans_handle; return TRUE; } @@ -162,15 +154,15 @@ class CI_DB_ibase_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans->depth > 0) + if (ibase_commit($this->_ibase_trans)) { + $this->_ibase_trans = NULL; return TRUE; } - return ibase_commit($this->_ibase_trans); + return FALSE; } // -------------------------------------------------------------------- @@ -180,15 +172,15 @@ class CI_DB_ibase_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if (ibase_rollback($this->_ibase_trans)) { + $this->_ibase_trans = NULL; return TRUE; } - return ibase_rollback($this->_ibase_trans); + return FALSE; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/ibase/index.html b/system/database/drivers/ibase/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/ibase/index.html +++ b/system/database/drivers/ibase/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/index.html b/system/database/drivers/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/index.html +++ b/system/database/drivers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/mssql/index.html b/system/database/drivers/mssql/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/mssql/index.html +++ b/system/database/drivers/mssql/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php index 8f15d8d69..883973ae1 100644 --- a/system/database/drivers/mssql/mssql_driver.php +++ b/system/database/drivers/mssql/mssql_driver.php @@ -182,22 +182,10 @@ class CI_DB_mssql_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - return $this->simple_query('BEGIN TRAN'); } @@ -208,14 +196,8 @@ class CI_DB_mssql_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->simple_query('COMMIT TRAN'); } @@ -226,14 +208,8 @@ class CI_DB_mssql_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->simple_query('ROLLBACK TRAN'); } @@ -381,9 +357,19 @@ class CI_DB_mssql_driver extends CI_DB { */ public function error() { - $query = $this->query('SELECT @@ERROR AS code'); - $query = $query->row(); - return array('code' => $query->code, 'message' => mssql_get_last_message()); + // We need this because the error info is discarded by the + // server the first time you request it, and query() already + // calls error() once for logging purposes when a query fails. + static $error = array('code' => 0, 'message' => NULL); + + $message = mssql_get_last_message(); + if ( ! empty($message)) + { + $error['code'] = $this->query('SELECT @@ERROR AS code')->row()->code; + $error['message'] = $message; + } + + return $error; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/mysql/index.html b/system/database/drivers/mysql/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/mysql/index.html +++ b/system/database/drivers/mysql/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php index f8e9b6d61..9c630d0d6 100644 --- a/system/database/drivers/mysql/mysql_driver.php +++ b/system/database/drivers/mysql/mysql_driver.php @@ -223,10 +223,6 @@ class CI_DB_mysql_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } if ( ! $this->conn_id OR ($version = mysql_get_server_info($this->conn_id)) === FALSE) { @@ -276,25 +272,12 @@ class CI_DB_mysql_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - $this->simple_query('SET AUTOCOMMIT=0'); - $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK - return TRUE; + return $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK } // -------------------------------------------------------------------- @@ -304,17 +287,15 @@ class CI_DB_mysql_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if ($this->simple_query('COMMIT')) { + $this->simple_query('SET AUTOCOMMIT=1'); return TRUE; } - $this->simple_query('COMMIT'); - $this->simple_query('SET AUTOCOMMIT=1'); - return TRUE; + return FALSE; } // -------------------------------------------------------------------- @@ -324,17 +305,15 @@ class CI_DB_mysql_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if ($this->simple_query('ROLLBACK')) { + $this->simple_query('SET AUTOCOMMIT=1'); return TRUE; } - $this->simple_query('ROLLBACK'); - $this->simple_query('SET AUTOCOMMIT=1'); - return TRUE; + return FALSE; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/mysqli/index.html b/system/database/drivers/mysqli/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/mysqli/index.html +++ b/system/database/drivers/mysqli/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php index 22a8ba678..827470078 100644 --- a/system/database/drivers/mysqli/mysqli_driver.php +++ b/system/database/drivers/mysqli/mysqli_driver.php @@ -102,7 +102,6 @@ class CI_DB_mysqli_driver extends CI_DB { * * @param bool $persistent * @return object - * @todo SSL support */ public function db_connect($persistent = FALSE) { @@ -132,8 +131,52 @@ class CI_DB_mysqli_driver extends CI_DB { $mysqli->options(MYSQLI_INIT_COMMAND, 'SET SESSION sql_mode="STRICT_ALL_TABLES"'); } - return $mysqli->real_connect($hostname, $this->username, $this->password, $this->database, $port, $socket, $client_flags) - ? $mysqli : FALSE; + if (is_array($this->encrypt)) + { + $ssl = array(); + empty($this->encrypt['ssl_key']) OR $ssl['key'] = $this->encrypt['ssl_key']; + empty($this->encrypt['ssl_cert']) OR $ssl['cert'] = $this->encrypt['ssl_cert']; + empty($this->encrypt['ssl_ca']) OR $ssl['ca'] = $this->encrypt['ssl_ca']; + empty($this->encrypt['ssl_capath']) OR $ssl['capath'] = $this->encrypt['ssl_capath']; + empty($this->encrypt['ssl_cipher']) OR $ssl['cipher'] = $this->encrypt['ssl_cipher']; + + if ( ! empty($ssl)) + { + if ( ! empty($this->encrypt['ssl_verify']) && defined('MYSQLI_OPT_SSL_VERIFY_SERVER_CERT')) + { + $mysqli->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, TRUE); + } + + $client_flags |= MYSQLI_CLIENT_SSL; + $mysqli->ssl_set( + isset($ssl['key']) ? $ssl['key'] : NULL, + isset($ssl['cert']) ? $ssl['cert'] : NULL, + isset($ssl['ca']) ? $ssl['ca'] : NULL, + isset($ssl['capath']) ? $ssl['capath'] : NULL, + isset($ssl['cipher']) ? $ssl['cipher'] : NULL + ); + } + } + + if ($mysqli->real_connect($hostname, $this->username, $this->password, $this->database, $port, $socket, $client_flags)) + { + // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails + if ( + ($client_flags & MYSQLI_CLIENT_SSL) + && version_compare($mysqli->client_info, '5.7.3', '<=') + && empty($mysqli->query("SHOW STATUS LIKE 'ssl_cipher'")->fetch_object()->Value) + ) + { + $mysqli->close(); + $message = 'MySQLi was configured for an SSL connection, but got an unencrypted connection instead!'; + log_message('error', $message); + return ($this->db->db_debug) ? $this->db->display_error($message, '', TRUE) : FALSE; + } + + return $mysqli; + } + + return FALSE; } // -------------------------------------------------------------------- @@ -204,10 +247,6 @@ class CI_DB_mysqli_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } return $this->data_cache['version'] = $this->conn_id->server_info; } @@ -252,22 +291,10 @@ class CI_DB_mysqli_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - $this->conn_id->autocommit(FALSE); return is_php('5.5') ? $this->conn_id->begin_transaction() @@ -281,14 +308,8 @@ class CI_DB_mysqli_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - if ($this->conn_id->commit()) { $this->conn_id->autocommit(TRUE); @@ -305,14 +326,8 @@ class CI_DB_mysqli_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - if ($this->conn_id->rollback()) { $this->conn_id->autocommit(TRUE); diff --git a/system/database/drivers/oci8/index.html b/system/database/drivers/oci8/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/oci8/index.html +++ b/system/database/drivers/oci8/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php index b87b41112..916ddeb90 100644 --- a/system/database/drivers/oci8/oci8_driver.php +++ b/system/database/drivers/oci8/oci8_driver.php @@ -102,6 +102,14 @@ class CI_DB_oci8_driver extends CI_DB { // -------------------------------------------------------------------- /** + * Reset $stmt_id flag + * + * Used by stored_procedure() to prevent _execute() from + * re-setting the statement ID. + */ + protected $_reset_stmt_id = TRUE; + + /** * List of reserved identifiers * * Identifiers that must NOT be escaped. @@ -243,10 +251,6 @@ class CI_DB_oci8_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } if ( ! $this->conn_id OR ($version = oci_server_version($this->conn_id)) === FALSE) { @@ -269,26 +273,13 @@ class CI_DB_oci8_driver extends CI_DB { /* Oracle must parse the query before it is run. All of the actions with * the query are based on the statement id returned by oci_parse(). */ - $this->stmt_id = FALSE; - $this->_set_stmt_id($sql); - oci_set_prefetch($this->stmt_id, 1000); - return oci_execute($this->stmt_id, $this->commit_mode); - } - - // -------------------------------------------------------------------- - - /** - * Generate a statement ID - * - * @param string $sql an SQL query - * @return void - */ - protected function _set_stmt_id($sql) - { - if ( ! is_resource($this->stmt_id)) + if ($this->_reset_stmt_id === TRUE) { $this->stmt_id = oci_parse($this->conn_id, $sql); } + + oci_set_prefetch($this->stmt_id, 1000); + return oci_execute($this->stmt_id, $this->commit_mode); } // -------------------------------------------------------------------- @@ -315,22 +306,22 @@ class CI_DB_oci8_driver extends CI_DB { * * params array keys * - * KEY OPTIONAL NOTES - * name no the name of the parameter should be in :<param_name> format - * value no the value of the parameter. If this is an OUT or IN OUT parameter, - * this should be a reference to a variable - * type yes the type of the parameter - * length yes the max size of the parameter + * KEY OPTIONAL NOTES + * name no the name of the parameter should be in :<param_name> format + * value no the value of the parameter. If this is an OUT or IN OUT parameter, + * this should be a reference to a variable + * type yes the type of the parameter + * length yes the max size of the parameter */ - public function stored_procedure($package, $procedure, $params) + public function stored_procedure($package, $procedure, array $params) { - if ($package === '' OR $procedure === '' OR ! is_array($params)) + if ($package === '' OR $procedure === '') { log_message('error', 'Invalid query: '.$package.'.'.$procedure); return ($this->db_debug) ? $this->display_error('db_invalid_query') : FALSE; } - // build the query string + // Build the query string $sql = 'BEGIN '.$package.'.'.$procedure.'('; $have_cursor = FALSE; @@ -345,10 +336,12 @@ class CI_DB_oci8_driver extends CI_DB { } $sql = trim($sql, ',').'); END;'; - $this->stmt_id = FALSE; - $this->_set_stmt_id($sql); + $this->_reset_stmt_id = FALSE; + $this->stmt_id = oci_parse($this->conn_id, $sql); $this->_bind_params($params); - return $this->query($sql, FALSE, $have_cursor); + $result = $this->query($sql, FALSE, $have_cursor); + $this->_reset_stmt_id = TRUE; + return $result; } // -------------------------------------------------------------------- @@ -385,27 +378,10 @@ class CI_DB_oci8_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - $this->commit_mode = is_php('5.3.2') ? OCI_NO_AUTO_COMMIT : OCI_DEFAULT; return TRUE; } @@ -417,20 +393,10 @@ class CI_DB_oci8_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - if ( ! $this->trans_enabled) - { - return TRUE; - } - - // When transactions are nested we only begin/commit/rollback the outermost ones - if ($this->_trans_depth > 0) - { - return TRUE; - } - $this->commit_mode = OCI_COMMIT_ON_SUCCESS; + return oci_commit($this->conn_id); } @@ -441,14 +407,8 @@ class CI_DB_oci8_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - $this->commit_mode = OCI_COMMIT_ON_SUCCESS; return oci_rollback($this->conn_id); } @@ -577,7 +537,7 @@ class CI_DB_oci8_driver extends CI_DB { { $default = ''; } - $retval[$i]->default = $query[$i]->COLUMN_DEFAULT; + $retval[$i]->default = $default; } return $retval; diff --git a/system/database/drivers/odbc/index.html b/system/database/drivers/odbc/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/odbc/index.html +++ b/system/database/drivers/odbc/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php index f5d77a147..409284b44 100644 --- a/system/database/drivers/odbc/odbc_driver.php +++ b/system/database/drivers/odbc/odbc_driver.php @@ -143,22 +143,10 @@ class CI_DB_odbc_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - return odbc_autocommit($this->conn_id, FALSE); } @@ -169,17 +157,15 @@ class CI_DB_odbc_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if (odbc_commit($this->conn_id)) { + odbc_autocommit($this->conn_id, TRUE); return TRUE; } - $ret = odbc_commit($this->conn_id); - odbc_autocommit($this->conn_id, TRUE); - return $ret; + return FALSE; } // -------------------------------------------------------------------- @@ -189,17 +175,15 @@ class CI_DB_odbc_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) + if (odbc_rollback($this->conn_id)) { + odbc_autocommit($this->conn_id, TRUE); return TRUE; } - $ret = odbc_rollback($this->conn_id); - odbc_autocommit($this->conn_id, TRUE); - return $ret; + return FALSE; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/pdo/index.html b/system/database/drivers/pdo/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/pdo/index.html +++ b/system/database/drivers/pdo/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php index 68aeb0eef..8c5a5e7e3 100644 --- a/system/database/drivers/pdo/pdo_driver.php +++ b/system/database/drivers/pdo/pdo_driver.php @@ -156,10 +156,6 @@ class CI_DB_pdo_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } // Not all subdrivers support the getAttribute() method try @@ -190,22 +186,10 @@ class CI_DB_pdo_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - return $this->conn_id->beginTransaction(); } @@ -216,14 +200,8 @@ class CI_DB_pdo_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->conn_id->commit(); } @@ -234,14 +212,8 @@ class CI_DB_pdo_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->conn_id->rollBack(); } diff --git a/system/database/drivers/pdo/subdrivers/index.html b/system/database/drivers/pdo/subdrivers/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/pdo/subdrivers/index.html +++ b/system/database/drivers/pdo/subdrivers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php index cf5a0c77f..7a767ef40 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_4d_driver.php @@ -158,7 +158,7 @@ class CI_DB_pdo_4d_driver extends CI_DB_pdo_driver { * @param string $table * @param array $values * @return string - */ + */ protected function _update($table, $values) { $this->qb_limit = FALSE; diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php index c8d7c770d..ae2b9983b 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php @@ -154,7 +154,7 @@ class CI_DB_pdo_dblib_driver extends CI_DB_pdo_driver { */ protected function _list_tables($prefix_limit = FALSE) { - return 'SELECT '.$this->escape_identifiers('name') + $sql = 'SELECT '.$this->escape_identifiers('name') .' FROM '.$this->escape_identifiers('sysobjects') .' WHERE '.$this->escape_identifiers('type')." = 'U'"; @@ -227,7 +227,7 @@ class CI_DB_pdo_dblib_driver extends CI_DB_pdo_driver { * @param string $table * @param array $values * @return string - */ + */ protected function _update($table, $values) { $this->qb_limit = FALSE; diff --git a/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php index 3bd5c9354..0bafde861 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_firebird_driver.php @@ -211,7 +211,7 @@ class CI_DB_pdo_firebird_driver extends CI_DB_pdo_driver { * * @param string $table * @return string - */ + */ protected function _truncate($table) { return 'DELETE FROM '.$table; diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php index 67dc5f5ec..e9d25cebc 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php @@ -119,7 +119,6 @@ class CI_DB_pdo_mysql_driver extends CI_DB_pdo_driver { * * @param bool $persistent * @return object - * @todo SSL support */ public function db_connect($persistent = FALSE) { @@ -151,7 +150,59 @@ class CI_DB_pdo_mysql_driver extends CI_DB_pdo_driver { $this->options[PDO::MYSQL_ATTR_COMPRESS] = TRUE; } - return parent::db_connect($persistent); + // SSL support was added to PDO_MYSQL in PHP 5.3.7 + if (is_array($this->encrypt) && is_php('5.3.7')) + { + $ssl = array(); + empty($this->encrypt['ssl_key']) OR $ssl[PDO::MYSQL_ATTR_SSL_KEY] = $this->encrypt['ssl_key']; + empty($this->encrypt['ssl_cert']) OR $ssl[PDO::MYSQL_ATTR_SSL_CERT] = $this->encrypt['ssl_cert']; + empty($this->encrypt['ssl_ca']) OR $ssl[PDO::MYSQL_ATTR_SSL_CA] = $this->encrypt['ssl_ca']; + empty($this->encrypt['ssl_capath']) OR $ssl[PDO::MYSQL_ATTR_SSL_CAPATH] = $this->encrypt['ssl_capath']; + empty($this->encrypt['ssl_cipher']) OR $ssl[PDO::MYSQL_ATTR_SSL_CIPHER] = $this->encrypt['ssl_cipher']; + + // DO NOT use array_merge() here! + // It re-indexes numeric keys and the PDO_MYSQL_ATTR_SSL_* constants are integers. + empty($ssl) OR $this->options += $ssl; + } + + // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails + if ( + ($pdo = parent::db_connect($persistent)) !== FALSE + && ! empty($ssl) + && version_compare($pdo->getAttribute(PDO::ATTR_CLIENT_VERSION), '5.7.3', '<=') + && empty($pdo->query("SHOW STATUS LIKE 'ssl_cipher'")->fetchObject()->Value) + ) + { + $message = 'PDO_MYSQL was configured for an SSL connection, but got an unencrypted connection instead!'; + log_message('error', $message); + return ($this->db->db_debug) ? $this->db->display_error($message, '', TRUE) : FALSE; + } + + return $pdo; + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @param string $database + * @return bool + */ + public function db_select($database = '') + { + if ($database === '') + { + $database = $this->database; + } + + if (FALSE !== $this->simple_query('USE '.$this->escape_identifiers($database))) + { + $this->database = $database; + return TRUE; + } + + return FALSE; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php index f07f49f84..409e6501b 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_driver.php @@ -121,17 +121,31 @@ class CI_DB_pdo_sqlite_driver extends CI_DB_pdo_driver { // -------------------------------------------------------------------- /** - * Show column query + * Fetch Field Names * - * Generates a platform-specific query string so that the column names can be fetched - * - * @param string $table - * @return string + * @param string $table Table name + * @return array */ - protected function _list_columns($table = '') + public function list_fields($table) { - // Not supported - return FALSE; + // Is there a cached result? + if (isset($this->data_cache['field_names'][$table])) + { + return $this->data_cache['field_names'][$table]; + } + + if (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE) + { + return FALSE; + } + + $this->data_cache['field_names'][$table] = array(); + foreach ($result->result_array() as $row) + { + $this->data_cache['field_names'][$table][] = $row['name']; + } + + return $this->data_cache['field_names'][$table]; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php b/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php index 28faaddb7..15afbdef5 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php +++ b/system/database/drivers/pdo/subdrivers/pdo_sqlite_forge.php @@ -89,6 +89,7 @@ class CI_DB_pdo_sqlite_forge extends CI_DB_pdo_forge { if (version_compare($this->db->version(), '3.3', '<')) { $this->_create_table_if = FALSE; + $this->_drop_table_if = FALSE; } } diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php index b5bdbf91a..f8ae5f6db 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_driver.php @@ -183,7 +183,7 @@ class CI_DB_pdo_sqlsrv_driver extends CI_DB_pdo_driver { */ protected function _list_tables($prefix_limit = FALSE) { - return 'SELECT '.$this->escape_identifiers('name') + $sql = 'SELECT '.$this->escape_identifiers('name') .' FROM '.$this->escape_identifiers('sysobjects') .' WHERE '.$this->escape_identifiers('type')." = 'U'"; diff --git a/system/database/drivers/postgre/index.html b/system/database/drivers/postgre/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/postgre/index.html +++ b/system/database/drivers/postgre/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php index 3f3e3f018..b1df326f7 100644 --- a/system/database/drivers/postgre/postgre_driver.php +++ b/system/database/drivers/postgre/postgre_driver.php @@ -213,10 +213,6 @@ class CI_DB_postgre_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } if ( ! $this->conn_id OR ($pg_version = pg_version($this->conn_id)) === FALSE) { @@ -251,22 +247,10 @@ class CI_DB_postgre_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - return (bool) pg_query($this->conn_id, 'BEGIN'); } @@ -277,14 +261,8 @@ class CI_DB_postgre_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return (bool) pg_query($this->conn_id, 'COMMIT'); } @@ -295,14 +273,8 @@ class CI_DB_postgre_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return (bool) pg_query($this->conn_id, 'ROLLBACK'); } diff --git a/system/database/drivers/sqlite/index.html b/system/database/drivers/sqlite/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/sqlite/index.html +++ b/system/database/drivers/sqlite/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php index 9d9caa0b4..e000a8e50 100644 --- a/system/database/drivers/sqlite/sqlite_driver.php +++ b/system/database/drivers/sqlite/sqlite_driver.php @@ -122,24 +122,11 @@ class CI_DB_sqlite_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - - $this->simple_query('BEGIN TRANSACTION'); - return TRUE; + return $this->simple_query('BEGIN TRANSACTION'); } // -------------------------------------------------------------------- @@ -149,16 +136,9 @@ class CI_DB_sqlite_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('COMMIT'); - return TRUE; + return $this->simple_query('COMMIT'); } // -------------------------------------------------------------------- @@ -168,16 +148,9 @@ class CI_DB_sqlite_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - $this->simple_query('ROLLBACK'); - return TRUE; + return $this->simple_query('ROLLBACK'); } // -------------------------------------------------------------------- diff --git a/system/database/drivers/sqlite3/index.html b/system/database/drivers/sqlite3/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/sqlite3/index.html +++ b/system/database/drivers/sqlite3/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/sqlite3/sqlite3_driver.php b/system/database/drivers/sqlite3/sqlite3_driver.php index fdbe94939..73e453785 100644 --- a/system/database/drivers/sqlite3/sqlite3_driver.php +++ b/system/database/drivers/sqlite3/sqlite3_driver.php @@ -134,22 +134,10 @@ class CI_DB_sqlite3_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - return $this->conn_id->exec('BEGIN TRANSACTION'); } @@ -160,14 +148,8 @@ class CI_DB_sqlite3_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->conn_id->exec('END TRANSACTION'); } @@ -178,14 +160,8 @@ class CI_DB_sqlite3_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return $this->conn_id->exec('ROLLBACK'); } @@ -247,17 +223,31 @@ class CI_DB_sqlite3_driver extends CI_DB { // -------------------------------------------------------------------- /** - * Show column query + * Fetch Field Names * - * Generates a platform-specific query string so that the column names can be fetched - * - * @param string $table - * @return string + * @param string $table Table name + * @return array */ - protected function _list_columns($table = '') + public function list_fields($table) { - // Not supported - return FALSE; + // Is there a cached result? + if (isset($this->data_cache['field_names'][$table])) + { + return $this->data_cache['field_names'][$table]; + } + + if (($result = $this->query('PRAGMA TABLE_INFO('.$this->protect_identifiers($table, TRUE, NULL, FALSE).')')) === FALSE) + { + return FALSE; + } + + $this->data_cache['field_names'][$table] = array(); + foreach ($result->result_array() as $row) + { + $this->data_cache['field_names'][$table][] = $row['name']; + } + + return $this->data_cache['field_names'][$table]; } // -------------------------------------------------------------------- diff --git a/system/database/drivers/sqlite3/sqlite3_forge.php b/system/database/drivers/sqlite3/sqlite3_forge.php index 69f65b6f3..24690ba20 100644 --- a/system/database/drivers/sqlite3/sqlite3_forge.php +++ b/system/database/drivers/sqlite3/sqlite3_forge.php @@ -74,7 +74,8 @@ class CI_DB_sqlite3_forge extends CI_DB_forge { if (version_compare($this->db->version(), '3.3', '<')) { - $this->create_table_if = FALSE; + $this->_create_table_if = FALSE; + $this->_drop_table_if = FALSE; } } diff --git a/system/database/drivers/sqlsrv/index.html b/system/database/drivers/sqlsrv/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/drivers/sqlsrv/index.html +++ b/system/database/drivers/sqlsrv/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php index 8f17c8f96..414669a4b 100644 --- a/system/database/drivers/sqlsrv/sqlsrv_driver.php +++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php @@ -141,13 +141,14 @@ class CI_DB_sqlsrv_driver extends CI_DB { unset($connection['UID'], $connection['PWD']); } - $this->conn_id = sqlsrv_connect($this->hostname, $connection); - - // Determine how identifiers are escaped - $query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi'); - $query = $query->row_array(); - $this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi']; - $this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']'); + if (FALSE !== ($this->conn_id = sqlsrv_connect($this->hostname, $connection))) + { + // Determine how identifiers are escaped + $query = $this->query('SELECT CASE WHEN (@@OPTIONS | 256) = @@OPTIONS THEN 1 ELSE 0 END AS qi'); + $query = $query->row_array(); + $this->_quoted_identifier = empty($query) ? FALSE : (bool) $query['qi']; + $this->_escape_char = ($this->_quoted_identifier) ? '"' : array('[', ']'); + } return $this->conn_id; } @@ -196,22 +197,10 @@ class CI_DB_sqlsrv_driver extends CI_DB { /** * Begin Transaction * - * @param bool $test_mode * @return bool */ - public function trans_begin($test_mode = FALSE) + protected function _trans_begin() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - - // Reset the transaction failure flag. - // If the $test_mode flag is set to TRUE transactions will be rolled back - // even if the queries produce a successful result. - $this->_trans_failure = ($test_mode === TRUE); - return sqlsrv_begin_transaction($this->conn_id); } @@ -222,14 +211,8 @@ class CI_DB_sqlsrv_driver extends CI_DB { * * @return bool */ - public function trans_commit() + protected function _trans_commit() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return sqlsrv_commit($this->conn_id); } @@ -240,14 +223,8 @@ class CI_DB_sqlsrv_driver extends CI_DB { * * @return bool */ - public function trans_rollback() + protected function _trans_rollback() { - // When transactions are nested we only begin/commit/rollback the outermost ones - if ( ! $this->trans_enabled OR $this->_trans_depth > 0) - { - return TRUE; - } - return sqlsrv_rollback($this->conn_id); } @@ -274,9 +251,7 @@ class CI_DB_sqlsrv_driver extends CI_DB { */ public function insert_id() { - $query = $this->query('SELECT @@IDENTITY AS insert_id'); - $query = $query->row(); - return $query->insert_id; + return $this->query('SELECT SCOPE_IDENTITY() AS insert_id')->row()->insert_id; } // -------------------------------------------------------------------- @@ -292,10 +267,6 @@ class CI_DB_sqlsrv_driver extends CI_DB { { return $this->data_cache['version']; } - elseif ( ! $this->conn_id) - { - $this->initialize(); - } if ( ! $this->conn_id OR ($info = sqlsrv_server_info($this->conn_id)) === FALSE) { diff --git a/system/database/index.html b/system/database/index.html index c942a79ce..b702fbc39 100644 --- a/system/database/index.html +++ b/system/database/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/fonts/index.html b/system/fonts/index.html index c942a79ce..b702fbc39 100644 --- a/system/fonts/index.html +++ b/system/fonts/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/helpers/array_helper.php b/system/helpers/array_helper.php index e07b52bb5..2ce55b9c4 100644 --- a/system/helpers/array_helper.php +++ b/system/helpers/array_helper.php @@ -62,7 +62,7 @@ if ( ! function_exists('element')) * @param mixed * @return mixed depends on what the array contains */ - function element($item, $array, $default = NULL) + function element($item, array $array, $default = NULL) { return array_key_exists($item, $array) ? $array[$item] : $default; } @@ -99,7 +99,7 @@ if ( ! function_exists('elements')) * @param mixed * @return mixed depends on what the array contains */ - function elements($items, $array, $default = NULL) + function elements($items, array $array, $default = NULL) { $return = array(); diff --git a/system/helpers/captcha_helper.php b/system/helpers/captcha_helper.php index 201987ac8..85bcfb5a0 100644 --- a/system/helpers/captcha_helper.php +++ b/system/helpers/captcha_helper.php @@ -125,9 +125,94 @@ if ( ! function_exists('create_captcha')) if (empty($word)) { $word = ''; - for ($i = 0, $mt_rand_max = strlen($pool) - 1; $i < $word_length; $i++) + $pool_length = strlen($pool); + $rand_max = $pool_length - 1; + + // PHP7 or a suitable polyfill + if (function_exists('random_int')) + { + try + { + for ($i = 0; $i < $word_length; $i++) + { + $word .= $pool[random_int(0, $rand_max)]; + } + } + catch (Exception $e) + { + // This means fallback to the next possible + // alternative to random_int() + $word = ''; + } + } + } + + if (empty($word)) + { + // Nobody will have a larger character pool than + // 256 characters, but let's handle it just in case ... + // + // No, I do not care that the fallback to mt_rand() can + // handle it; if you trigger this, you're very obviously + // trying to break it. -- Narf + if ($pool_length > 256) + { + return FALSE; + } + + // We'll try using the operating system's PRNG first, + // which we can access through CI_Security::get_random_bytes() + $security = get_instance()->security; + + // To avoid numerous get_random_bytes() calls, we'll + // just try fetching as much bytes as we need at once. + if (($bytes = $security->get_random_bytes($pool_length)) !== FALSE) + { + $byte_index = $word_index = 0; + while ($word_index < $word_length) + { + if (($rand_index = unpack('C', $bytes[$byte_index++])) > $rand_max) + { + // Was this the last byte we have? + // If so, try to fetch more. + if ($byte_index === $pool_length) + { + // No failures should be possible if + // the first get_random_bytes() call + // didn't return FALSE, but still ... + for ($i = 0; $i < 5; $i++) + { + if (($bytes = $security->get_random_bytes($pool_length)) === FALSE) + { + continue; + } + + $byte_index = 0; + break; + } + + if ($bytes === FALSE) + { + // Sadly, this means fallback to mt_rand() + $word = ''; + break; + } + } + + continue; + } + + $word .= $pool[$rand_index]; + $word_index++; + } + } + } + + if (empty($word)) + { + for ($i = 0; $i < $word_length; $i++) { - $word .= $pool[mt_rand(0, $mt_rand_max)]; + $word .= $pool[mt_rand(0, $rand_max)]; } } elseif ( ! is_string($word)) diff --git a/system/helpers/download_helper.php b/system/helpers/download_helper.php index 95c94a1b8..73f6456c4 100644 --- a/system/helpers/download_helper.php +++ b/system/helpers/download_helper.php @@ -69,16 +69,14 @@ if ( ! function_exists('force_download')) } elseif ($data === NULL) { - if (@is_file($filename) && ($filesize = @filesize($filename)) !== FALSE) - { - $filepath = $filename; - $filename = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $filename)); - $filename = end($filename); - } - else + if ( ! @is_file($filename) OR ($filesize = @filesize($filename)) === FALSE) { return; } + + $filepath = $filename; + $filename = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $filename)); + $filename = end($filename); } else { @@ -140,14 +138,7 @@ if ( ! function_exists('force_download')) header('Expires: 0'); header('Content-Transfer-Encoding: binary'); header('Content-Length: '.$filesize); - - // Internet Explorer-specific headers - if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== FALSE) - { - header('Cache-Control: no-cache, no-store, must-revalidate'); - } - - header('Pragma: no-cache'); + header('Cache-Control: private, no-transform, no-store, must-revalidate'); // If we have raw data - just dump it if ($data !== NULL) diff --git a/system/helpers/file_helper.php b/system/helpers/file_helper.php index 8b15e60a5..f6cb1629a 100644 --- a/system/helpers/file_helper.php +++ b/system/helpers/file_helper.php @@ -54,7 +54,7 @@ if ( ! function_exists('read_file')) /** * Read File * - * Opens the file specfied in the path and returns it as a string. + * Opens the file specified in the path and returns it as a string. * * @todo Remove in version 3.1+. * @deprecated 3.0.0 It is now just an alias for PHP's native file_get_contents(). @@ -343,7 +343,7 @@ if ( ! function_exists('get_mime_by_extension')) if ( ! is_array($mimes)) { - $mimes =& get_mimes(); + $mimes = get_mimes(); if (empty($mimes)) { diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php index 53ee8eb11..fd807769a 100644 --- a/system/helpers/form_helper.php +++ b/system/helpers/form_helper.php @@ -197,7 +197,7 @@ if ( ! function_exists('form_input')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_input($data = '', $value = '', $extra = '') @@ -208,7 +208,7 @@ if ( ! function_exists('form_input')) 'value' => $value ); - return '<input '._parse_form_attributes($data, $defaults).$extra." />\n"; + return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n"; } } @@ -223,7 +223,7 @@ if ( ! function_exists('form_password')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_password($data = '', $value = '', $extra = '') @@ -245,7 +245,7 @@ if ( ! function_exists('form_upload')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_upload($data = '', $value = '', $extra = '') @@ -253,7 +253,8 @@ if ( ! function_exists('form_upload')) $defaults = array('type' => 'file', 'name' => ''); is_array($data) OR $data = array('name' => $data); $data['type'] = 'file'; - return '<input '._parse_form_attributes($data, $defaults).$extra." />\n"; + + return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n"; } } @@ -266,7 +267,7 @@ if ( ! function_exists('form_textarea')) * * @param mixed $data * @param string $value - * @param string $extra + * @param mixed $extra * @return string */ function form_textarea($data = '', $value = '', $extra = '') @@ -287,7 +288,9 @@ if ( ! function_exists('form_textarea')) unset($data['value']); // textareas don't use the value attribute } - return '<textarea '._parse_form_attributes($data, $defaults).$extra.'>'.html_escape($val)."</textarea>\n"; + return '<textarea '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).'>' + .html_escape($val) + ."</textarea>\n"; } } @@ -301,12 +304,13 @@ if ( ! function_exists('form_multiselect')) * @param string * @param array * @param mixed - * @param string + * @param mixed * @return string */ function form_multiselect($name = '', $options = array(), $selected = array(), $extra = '') { - if ( ! strpos($extra, 'multiple')) + $extra = _attributes_to_string($extra); + if (stripos($extra, 'multiple') === FALSE) { $extra .= ' multiple="multiple"'; } @@ -372,7 +376,7 @@ if ( ! function_exists('form_dropdown')) $extra = _attributes_to_string($extra); - $multiple = (count($selected) > 1 && strpos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : ''; + $multiple = (count($selected) > 1 && stripos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : ''; $form = '<select '.rtrim(_parse_form_attributes($data, $defaults)).$extra.$multiple.">\n"; @@ -420,7 +424,7 @@ if ( ! function_exists('form_checkbox')) * @param mixed * @param string * @param bool - * @param string + * @param mixed * @return string */ function form_checkbox($data = '', $value = '', $checked = FALSE, $extra = '') @@ -450,7 +454,7 @@ if ( ! function_exists('form_checkbox')) unset($defaults['checked']); } - return '<input '._parse_form_attributes($data, $defaults).$extra." />\n"; + return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n"; } } @@ -464,13 +468,14 @@ if ( ! function_exists('form_radio')) * @param mixed * @param string * @param bool - * @param string + * @param mixed * @return string */ function form_radio($data = '', $value = '', $checked = FALSE, $extra = '') { is_array($data) OR $data = array('name' => $data); $data['type'] = 'radio'; + return form_checkbox($data, $value, $checked, $extra); } } @@ -484,7 +489,7 @@ if ( ! function_exists('form_submit')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_submit($data = '', $value = '', $extra = '') @@ -495,7 +500,7 @@ if ( ! function_exists('form_submit')) 'value' => $value ); - return '<input '._parse_form_attributes($data, $defaults).$extra." />\n"; + return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n"; } } @@ -508,7 +513,7 @@ if ( ! function_exists('form_reset')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_reset($data = '', $value = '', $extra = '') @@ -519,7 +524,7 @@ if ( ! function_exists('form_reset')) 'value' => $value ); - return '<input '._parse_form_attributes($data, $defaults).$extra." />\n"; + return '<input '._parse_form_attributes($data, $defaults)._attributes_to_string($extra)." />\n"; } } @@ -532,7 +537,7 @@ if ( ! function_exists('form_button')) * * @param mixed * @param string - * @param string + * @param mixed * @return string */ function form_button($data = '', $content = '', $extra = '') @@ -548,7 +553,9 @@ if ( ! function_exists('form_button')) unset($data['content']); // content is not an attribute } - return '<button '._parse_form_attributes($data, $defaults).$extra.'>'.$content."</button>\n"; + return '<button '._parse_form_attributes($data, $defaults)._attributes_to_string($extra).'>' + .$content + ."</button>\n"; } } diff --git a/system/helpers/index.html b/system/helpers/index.html index c942a79ce..b702fbc39 100644 --- a/system/helpers/index.html +++ b/system/helpers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/helpers/inflector_helper.php b/system/helpers/inflector_helper.php index d8ed45df9..f2890059f 100644 --- a/system/helpers/inflector_helper.php +++ b/system/helpers/inflector_helper.php @@ -133,6 +133,7 @@ if ( ! function_exists('plural')) } $plural_rules = array( + '/(quiz)$/' => '\1zes', // quizzes '/^(ox)$/' => '\1\2en', // ox '/([m|l])ouse$/' => '\1ice', // mouse, louse '/(matr|vert|ind)ix|ex$/' => '\1ices', // matrix, vertex, index diff --git a/system/helpers/path_helper.php b/system/helpers/path_helper.php index c23ec6435..c96d0b8b3 100644 --- a/system/helpers/path_helper.php +++ b/system/helpers/path_helper.php @@ -61,7 +61,7 @@ if ( ! function_exists('set_realpath')) function set_realpath($path, $check_existance = FALSE) { // Security check to make sure the path is NOT a URL. No remote file inclusion! - if (preg_match('#^(http:\/\/|https:\/\/|www\.|ftp|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})#i', $path)) + if (preg_match('#^(http:\/\/|https:\/\/|www\.|ftp)#i', $path) OR filter_var($path, FILTER_VALIDATE_IP) === $path ) { show_error('The path you submitted must be a local server path, not a URL'); } diff --git a/system/helpers/string_helper.php b/system/helpers/string_helper.php index 5860e15fb..637835160 100644 --- a/system/helpers/string_helper.php +++ b/system/helpers/string_helper.php @@ -253,7 +253,7 @@ if ( ! function_exists('increment_string')) */ function increment_string($str, $separator = '_', $first = 1) { - preg_match('/(.+)'.$separator.'([0-9]+)$/', $str, $match); + preg_match('/(.+)'.preg_quote($separator, '/').'([0-9]+)$/', $str, $match); return isset($match[2]) ? $match[1].$separator.($match[2] + 1) : $str.$separator.$first; } } diff --git a/system/helpers/text_helper.php b/system/helpers/text_helper.php index f2290c895..fb47036f2 100644 --- a/system/helpers/text_helper.php +++ b/system/helpers/text_helper.php @@ -254,7 +254,7 @@ if ( ! function_exists('word_censor')) * word you've submitted. * * @param string the text string - * @param string the array of censoered words + * @param string the array of censored words * @param string the optional replacement value * @return string */ diff --git a/system/helpers/url_helper.php b/system/helpers/url_helper.php index bf623b000..d65f92f1b 100644 --- a/system/helpers/url_helper.php +++ b/system/helpers/url_helper.php @@ -161,7 +161,7 @@ if ( ! function_exists('anchor')) $site_url = is_array($uri) ? site_url($uri) - : preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri); + : (preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri)); if ($title === '') { @@ -474,7 +474,7 @@ if ( ! function_exists('url_title')) * @param string $str Input string * @param string $separator Word separator * (usually '-' or '_') - * @param bool $lowercase Wether to transform the output string to lowercase + * @param bool $lowercase Whether to transform the output string to lowercase * @return string */ function url_title($str, $separator = '-', $lowercase = FALSE) @@ -492,7 +492,7 @@ if ( ! function_exists('url_title')) $trans = array( '&.+?;' => '', - '[^a-z0-9 _-]' => '', + '[^\w\d _-]' => '', '\s+' => $separator, '('.$q_separator.')+' => $separator ); @@ -500,7 +500,7 @@ if ( ! function_exists('url_title')) $str = strip_tags($str); foreach ($trans as $key => $val) { - $str = preg_replace('#'.$key.'#i', $val, $str); + $str = preg_replace('#'.$key.'#i'.(UTF8_ENABLED ? 'u' : ''), $val, $str); } if ($lowercase === TRUE) diff --git a/system/index.html b/system/index.html index c942a79ce..b702fbc39 100644 --- a/system/index.html +++ b/system/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/language/english/index.html b/system/language/english/index.html index c942a79ce..b702fbc39 100644 --- a/system/language/english/index.html +++ b/system/language/english/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/language/index.html b/system/language/index.html index c942a79ce..b702fbc39 100644 --- a/system/language/index.html +++ b/system/language/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Cache/Cache.php b/system/libraries/Cache/Cache.php index 40ac70103..0c87a5628 100644 --- a/system/libraries/Cache/Cache.php +++ b/system/libraries/Cache/Cache.php @@ -100,28 +100,10 @@ class CI_Cache extends CI_Driver_Library { */ public function __construct($config = array()) { - $default_config = array( - 'adapter', - 'memcached' - ); - - foreach ($default_config as $key) - { - if (isset($config[$key])) - { - $param = '_'.$key; - - $this->{$param} = $config[$key]; - } - } - + isset($config['adapter']) && $this->_adapter = $config['adapter']; + isset($config['backup']) && $this->_backup_driver = $config['backup']; isset($config['key_prefix']) && $this->key_prefix = $config['key_prefix']; - if (isset($config['backup']) && in_array($config['backup'], $this->valid_drivers)) - { - $this->_backup_driver = $config['backup']; - } - // If the specified adapter isn't available, check the backup. if ( ! $this->is_supported($this->_adapter)) { @@ -196,7 +178,7 @@ class CI_Cache extends CI_Driver_Library { */ public function increment($id, $offset = 1) { - return $this->{$this->_adapter}->increment($id, $offset); + return $this->{$this->_adapter}->increment($this->key_prefix.$id, $offset); } // ------------------------------------------------------------------------ @@ -210,7 +192,7 @@ class CI_Cache extends CI_Driver_Library { */ public function decrement($id, $offset = 1) { - return $this->{$this->_adapter}->decrement($id, $offset); + return $this->{$this->_adapter}->decrement($this->key_prefix.$id, $offset); } // ------------------------------------------------------------------------ @@ -261,14 +243,13 @@ class CI_Cache extends CI_Driver_Library { */ public function is_supported($driver) { - static $support = array(); + static $support; - if ( ! isset($support[$driver])) + if ( ! isset($support, $support[$driver])) { $support[$driver] = $this->{$driver}->is_supported(); } return $support[$driver]; } - } diff --git a/system/libraries/Cache/drivers/Cache_file.php b/system/libraries/Cache/drivers/Cache_file.php index 68bc1ec96..c046f3b7d 100644 --- a/system/libraries/Cache/drivers/Cache_file.php +++ b/system/libraries/Cache/drivers/Cache_file.php @@ -267,7 +267,7 @@ class CI_Cache_file extends CI_Driver { */ protected function _get($id) { - if ( ! file_exists($this->_cache_path.$id)) + if ( ! is_file($this->_cache_path.$id)) { return FALSE; } diff --git a/system/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php index b90b561c9..59cf4685d 100644 --- a/system/libraries/Cache/drivers/Cache_memcached.php +++ b/system/libraries/Cache/drivers/Cache_memcached.php @@ -68,6 +68,76 @@ class CI_Cache_memcached extends CI_Driver { ) ); + // ------------------------------------------------------------------------ + + /** + * Class constructor + * + * Setup Memcache(d) + * + * @return void + */ + public function __construct() + { + // Try to load memcached server info from the config file. + $CI =& get_instance(); + $defaults = $this->_memcache_conf['default']; + + if ($CI->config->load('memcached', TRUE, TRUE)) + { + if (is_array($CI->config->config['memcached'])) + { + $this->_memcache_conf = array(); + + foreach ($CI->config->config['memcached'] as $name => $conf) + { + $this->_memcache_conf[$name] = $conf; + } + } + } + + if (class_exists('Memcached', FALSE)) + { + $this->_memcached = new Memcached(); + } + elseif (class_exists('Memcache', FALSE)) + { + $this->_memcached = new Memcache(); + } + else + { + log_message('error', 'Cache: Failed to create Memcache(d) object; extension not loaded?'); + } + + foreach ($this->_memcache_conf as $cache_server) + { + isset($cache_server['hostname']) OR $cache_server['hostname'] = $defaults['host']; + isset($cache_server['port']) OR $cache_server['port'] = $defaults['port']; + isset($cache_server['weight']) OR $cache_server['weight'] = $defaults['weight']; + + if (get_class($this->_memcached) === 'Memcache') + { + // Third parameter is persistance and defaults to TRUE. + $this->_memcached->addServer( + $cache_server['hostname'], + $cache_server['port'], + TRUE, + $cache_server['weight'] + ); + } + else + { + $this->_memcached->addServer( + $cache_server['hostname'], + $cache_server['port'], + $cache_server['weight'] + ); + } + } + } + + // ------------------------------------------------------------------------ + /** * Fetch from cache * @@ -205,75 +275,6 @@ class CI_Cache_memcached extends CI_Driver { // ------------------------------------------------------------------------ /** - * Setup memcached. - * - * @return bool - */ - protected function _setup_memcached() - { - // Try to load memcached server info from the config file. - $CI =& get_instance(); - $defaults = $this->_memcache_conf['default']; - - if ($CI->config->load('memcached', TRUE, TRUE)) - { - if (is_array($CI->config->config['memcached'])) - { - $this->_memcache_conf = array(); - - foreach ($CI->config->config['memcached'] as $name => $conf) - { - $this->_memcache_conf[$name] = $conf; - } - } - } - - if (class_exists('Memcached', FALSE)) - { - $this->_memcached = new Memcached(); - } - elseif (class_exists('Memcache', FALSE)) - { - $this->_memcached = new Memcache(); - } - else - { - log_message('error', 'Failed to create object for Memcached Cache; extension not loaded?'); - return FALSE; - } - - foreach ($this->_memcache_conf as $cache_server) - { - isset($cache_server['hostname']) OR $cache_server['hostname'] = $defaults['host']; - isset($cache_server['port']) OR $cache_server['port'] = $defaults['port']; - isset($cache_server['weight']) OR $cache_server['weight'] = $defaults['weight']; - - if (get_class($this->_memcached) === 'Memcache') - { - // Third parameter is persistance and defaults to TRUE. - $this->_memcached->addServer( - $cache_server['hostname'], - $cache_server['port'], - TRUE, - $cache_server['weight'] - ); - } - else - { - $this->_memcached->addServer( - $cache_server['hostname'], - $cache_server['port'], - $cache_server['weight'] - ); - } - } - - return TRUE; - } - - // ------------------------------------------------------------------------ - - /** * Is supported * * Returns FALSE if memcached is not supported on the system. @@ -283,13 +284,6 @@ class CI_Cache_memcached extends CI_Driver { */ public function is_supported() { - if ( ! extension_loaded('memcached') && ! extension_loaded('memcache')) - { - log_message('debug', 'The Memcached Extension must be loaded to use Memcached Cache.'); - return FALSE; - } - - return $this->_setup_memcached(); + return (extension_loaded('memcached') OR extension_loaded('memcache')); } - } diff --git a/system/libraries/Cache/drivers/Cache_redis.php b/system/libraries/Cache/drivers/Cache_redis.php index f2a41cc67..ea0059ff7 100644 --- a/system/libraries/Cache/drivers/Cache_redis.php +++ b/system/libraries/Cache/drivers/Cache_redis.php @@ -79,6 +79,63 @@ class CI_Cache_redis extends CI_Driver // ------------------------------------------------------------------------ /** + * Class constructor + * + * Setup Redis + * + * Loads Redis config file if present. Will halt execution + * if a Redis connection can't be established. + * + * @return void + * @see Redis::connect() + */ + public function __construct() + { + $config = array(); + $CI =& get_instance(); + + if ($CI->config->load('redis', TRUE, TRUE)) + { + $config = $CI->config->item('redis'); + } + + $config = array_merge(self::$_default_config, $config); + $this->_redis = new Redis(); + + try + { + if ($config['socket_type'] === 'unix') + { + $success = $this->_redis->connect($config['socket']); + } + else // tcp socket + { + $success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']); + } + + if ( ! $success) + { + log_message('error', 'Cache: Redis connection failed. Check your configuration.'); + } + + if (isset($config['password']) && ! $this->_redis->auth($config['password'])) + { + log_message('error', 'Cache: Redis authentication failed.'); + } + } + catch (RedisException $e) + { + log_message('error', 'Cache: Redis connection refused ('.$e->getMessage().')'); + } + + // Initialize the index of serialized values. + $serialized = $this->_redis->sMembers('_ci_redis_serialized'); + empty($serialized) OR $this->_serialized = array_flip($serialized); + } + + // ------------------------------------------------------------------------ + + /** * Get cache * * @param string Cache ID @@ -111,7 +168,7 @@ class CI_Cache_redis extends CI_Driver { if (is_array($data) OR is_object($data)) { - if ( ! $this->_redis->sAdd('_ci_redis_serialized', $id)) + if ( ! $this->_redis->sIsMember('_ci_redis_serialized', $id) && ! $this->_redis->sAdd('_ci_redis_serialized', $id)) { return FALSE; } @@ -125,9 +182,7 @@ class CI_Cache_redis extends CI_Driver $this->_redis->sRemove('_ci_redis_serialized', $id); } - return ($ttl) - ? $this->_redis->setex($id, $ttl, $data) - : $this->_redis->set($id, $data); + return $this->_redis->set($id, $data, $ttl); } // ------------------------------------------------------------------------ @@ -223,7 +278,7 @@ class CI_Cache_redis extends CI_Driver { $value = $this->get($key); - if ($value) + if ($value !== FALSE) { return array( 'expire' => time() + $this->_redis->ttl($key), @@ -243,78 +298,7 @@ class CI_Cache_redis extends CI_Driver */ public function is_supported() { - if (extension_loaded('redis')) - { - return $this->_setup_redis(); - } - else - { - log_message('debug', 'The Redis extension must be loaded to use Redis cache.'); - return FALSE; - } - } - - // ------------------------------------------------------------------------ - - /** - * Setup Redis config and connection - * - * Loads Redis config file if present. Will halt execution - * if a Redis connection can't be established. - * - * @return bool - * @see Redis::connect() - */ - protected function _setup_redis() - { - $config = array(); - $CI =& get_instance(); - - if ($CI->config->load('redis', TRUE, TRUE)) - { - $config += $CI->config->item('redis'); - } - - $config = array_merge(self::$_default_config, $config); - - $this->_redis = new Redis(); - - try - { - if ($config['socket_type'] === 'unix') - { - $success = $this->_redis->connect($config['socket']); - } - else // tcp socket - { - $success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']); - } - - if ( ! $success) - { - log_message('debug', 'Cache: Redis connection refused. Check the config.'); - return FALSE; - } - } - catch (RedisException $e) - { - log_message('debug', 'Cache: Redis connection refused ('.$e->getMessage().')'); - return FALSE; - } - - if (isset($config['password'])) - { - $this->_redis->auth($config['password']); - } - - // Initialize the index of serialized values. - $serialized = $this->_redis->sMembers('_ci_redis_serialized'); - if ( ! empty($serialized)) - { - $this->_serialized = array_flip($serialized); - } - - return TRUE; + return extension_loaded('redis'); } // ------------------------------------------------------------------------ @@ -333,5 +317,4 @@ class CI_Cache_redis extends CI_Driver $this->_redis->close(); } } - } diff --git a/system/libraries/Cache/drivers/Cache_wincache.php b/system/libraries/Cache/drivers/Cache_wincache.php index 528b2b9bf..9cc6ff016 100644 --- a/system/libraries/Cache/drivers/Cache_wincache.php +++ b/system/libraries/Cache/drivers/Cache_wincache.php @@ -194,7 +194,7 @@ class CI_Cache_wincache extends CI_Driver { */ public function is_supported() { - if ( ! extension_loaded('wincache')) + if ( ! extension_loaded('wincache') OR ! ini_get('wincache.ucenabled')) { log_message('debug', 'The Wincache PHP extension must be loaded to use Wincache Cache.'); return FALSE; diff --git a/system/libraries/Cache/drivers/index.html b/system/libraries/Cache/drivers/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/Cache/drivers/index.html +++ b/system/libraries/Cache/drivers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Cache/index.html b/system/libraries/Cache/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/Cache/index.html +++ b/system/libraries/Cache/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Calendar.php b/system/libraries/Calendar.php index 014daac58..f6a0c39c4 100644 --- a/system/libraries/Calendar.php +++ b/system/libraries/Calendar.php @@ -130,11 +130,7 @@ class CI_Calendar { public function __construct($config = array()) { $this->CI =& get_instance(); - - if ( ! in_array('calendar_lang.php', $this->CI->lang->is_loaded, TRUE)) - { - $this->CI->lang->load('calendar'); - } + $this->CI->lang->load('calendar'); empty($config) OR $this->initialize($config); diff --git a/system/libraries/Email.php b/system/libraries/Email.php index 4e0e0cd9f..ebff7567a 100644 --- a/system/libraries/Email.php +++ b/system/libraries/Email.php @@ -405,7 +405,7 @@ class CI_Email { * @param array $config = array() * @return void */ - public function __construct($config = array()) + public function __construct(array $config = array()) { $this->charset = config_item('charset'); @@ -804,11 +804,12 @@ class CI_Email { * * @param string * @param string - * @return void + * @return CI_Email */ public function set_header($header, $value) { $this->_headers[$header] = str_replace(array("\n", "\r"), '', $value); + return $this; } // -------------------------------------------------------------------- @@ -1562,11 +1563,10 @@ class CI_Email { if ($this->charset === 'UTF-8') { - if (MB_ENABLED === TRUE) - { - return mb_encode_mimeheader($str, $this->charset, 'Q', $this->crlf); - } - elseif (ICONV_ENABLED === TRUE) + // Note: We used to have mb_encode_mimeheader() as the first choice + // here, but it turned out to be buggy and unreliable. DO NOT + // re-add it! -- Narf + if (ICONV_ENABLED === TRUE) { $output = @iconv_mime_encode('', $str, array( @@ -1589,6 +1589,10 @@ class CI_Email { $chars = iconv_strlen($str, 'UTF-8'); } + elseif (MB_ENABLED === TRUE) + { + $chars = mb_strlen($str, 'UTF-8'); + } } // We might already have this set for UTF-8 @@ -1868,20 +1872,26 @@ class CI_Email { return FALSE; } - $this->_send_command('from', $this->clean_email($this->_headers['From'])); + if ( ! $this->_send_command('from', $this->clean_email($this->_headers['From']))) + { + return FALSE; + } foreach ($this->_recipients as $val) { - $this->_send_command('to', $val); + if ( ! $this->_send_command('to', $val)) + { + return FALSE; + } } if (count($this->_cc_array) > 0) { foreach ($this->_cc_array as $val) { - if ($val !== '') + if ($val !== '' && ! $this->_send_command('to', $val)) { - $this->_send_command('to', $val); + return FALSE; } } } @@ -1890,14 +1900,17 @@ class CI_Email { { foreach ($this->_bcc_array as $val) { - if ($val !== '') + if ($val !== '' && ! $this->_send_command('to', $val)) { - $this->_send_command('to', $val); + return FALSE; } } } - $this->_send_command('data'); + if ( ! $this->_send_command('data')) + { + return FALSE; + } // perform dot transformation on any lines that begin with a dot $this->_send_data($this->_header_str.preg_replace('/^\./m', '..$1', $this->_finalbody)); @@ -2126,12 +2139,32 @@ class CI_Email { protected function _send_data($data) { $data .= $this->newline; - for ($written = 0, $length = strlen($data); $written < $length; $written += $result) + for ($written = $timestamp = 0, $length = strlen($data); $written < $length; $written += $result) { if (($result = fwrite($this->_smtp_connect, substr($data, $written))) === FALSE) { break; } + // See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951 + elseif ($result === 0) + { + if ($timestamp === 0) + { + $timestamp = time(); + } + elseif ($timestamp < (time() - $this->smtp_timeout)) + { + $result = FALSE; + break; + } + + usleep(250000); + continue; + } + else + { + $timestamp = 0; + } } if ($result === FALSE) @@ -2172,11 +2205,22 @@ class CI_Email { /** * Get Hostname * + * There are only two legal types of hostname - either a fully + * qualified domain name (eg: "mail.example.com") or an IP literal + * (eg: "[1.2.3.4]"). + * + * @link https://tools.ietf.org/html/rfc5321#section-2.3.5 + * @link http://cbl.abuseat.org/namingproblems.html * @return string */ protected function _get_hostname() { - return isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain'; + if (isset($_SERVER['SERVER_NAME'])) + { + return $_SERVER['SERVER_NAME']; + } + + return isset($_SERVER['SERVER_ADDR']) ? '['.$_SERVER['SERVER_ADDR'].']' : '[127.0.0.1]'; } // -------------------------------------------------------------------- diff --git a/system/libraries/Encrypt.php b/system/libraries/Encrypt.php index 5faf1f206..a46d4f462 100644 --- a/system/libraries/Encrypt.php +++ b/system/libraries/Encrypt.php @@ -65,7 +65,7 @@ class CI_Encrypt { protected $_hash_type = 'sha1'; /** - * Flag for the existance of mcrypt + * Flag for the existence of mcrypt * * @var bool */ diff --git a/system/libraries/Encryption.php b/system/libraries/Encryption.php index e3e68139a..f3e039881 100644 --- a/system/libraries/Encryption.php +++ b/system/libraries/Encryption.php @@ -121,7 +121,7 @@ class CI_Encryption { ); /** - * List of supported HMAC algorightms + * List of supported HMAC algorithms * * name => digest size pairs * diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php index f161b40e7..a158225ee 100644 --- a/system/libraries/Form_validation.php +++ b/system/libraries/Form_validation.php @@ -198,22 +198,20 @@ class CI_Form_validation { return $this; } - // No fields? Nothing to do... - if ( ! is_string($field) OR $field === '') + // No fields or no rules? Nothing to do... + if ( ! is_string($field) OR $field === '' OR empty($rules)) { return $this; } elseif ( ! is_array($rules)) { // BC: Convert pipe-separated rules string to an array - if (is_string($rules)) - { - $rules = explode('|', $rules); - } - else + if ( ! is_string($rules)) { return $this; } + + $rules = preg_split('/\|(?![^\[]*\])/', $rules); } // If the field label wasn't passed we use the field name @@ -463,7 +461,7 @@ class CI_Form_validation { { $this->_field_data[$field]['postdata'] = $this->_reduce_array($validation_array, $row['keys']); } - elseif (isset($validation_array[$field]) && $validation_array[$field] !== '') + elseif (isset($validation_array[$field])) { $this->_field_data[$field]['postdata'] = $validation_array[$field]; } @@ -620,6 +618,12 @@ class CI_Form_validation { $rules = array(1 => $rule); break; } + elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1])) + { + $callback = TRUE; + $rules = array(array($rule[0], $rule[1])); + break; + } } if ( ! $callback) @@ -817,11 +821,10 @@ class CI_Form_validation { // Callable rules might not have named error messages if ( ! is_string($rule)) { - return; + $line = $this->CI->lang->line('form_validation_error_message_not_set').'(Anonymous function)'; } - // Check if a custom message is defined - if (isset($this->_field_data[$row['field']]['errors'][$rule])) + elseif (isset($this->_field_data[$row['field']]['errors'][$rule])) { $line = $this->_field_data[$row['field']]['errors'][$rule]; } @@ -831,7 +834,7 @@ class CI_Form_validation { // DEPRECATED support for non-prefixed keys && FALSE === ($line = $this->CI->lang->line($rule, FALSE))) { - $line = $this->CI->lang->line('form_validation_error_message_not_set'); + $line = $this->CI->lang->line('form_validation_error_message_not_set').'('.$rule.')'; } } else @@ -872,17 +875,11 @@ class CI_Form_validation { */ protected function _translate_fieldname($fieldname) { - // Do we need to translate the field name? - // We look for the prefix lang: to determine this - if (sscanf($fieldname, 'lang:%s', $line) === 1) + // Do we need to translate the field name? We look for the prefix 'lang:' to determine this + // If we find one, but there's no translation for the string - just return it + if (sscanf($fieldname, 'lang:%s', $line) === 1 && FALSE === ($fieldname = $this->CI->lang->line($line, FALSE))) { - // Were we able to translate the field name? If not we use $line - if (FALSE === ($fieldname = $this->CI->lang->line('form_validation_'.$line)) - // DEPRECATED support for non-prefixed keys - && FALSE === ($fieldname = $this->CI->lang->line($line, FALSE))) - { - return $line; - } + return $line; } return $fieldname; @@ -1151,9 +1148,7 @@ class CI_Form_validation { return FALSE; } - return (MB_ENABLED === TRUE) - ? ($val <= mb_strlen($str)) - : ($val <= strlen($str)); + return ($val <= mb_strlen($str)); } // -------------------------------------------------------------------- @@ -1172,9 +1167,7 @@ class CI_Form_validation { return FALSE; } - return (MB_ENABLED === TRUE) - ? ($val >= mb_strlen($str)) - : ($val >= strlen($str)); + return ($val >= mb_strlen($str)); } // -------------------------------------------------------------------- @@ -1193,9 +1186,7 @@ class CI_Form_validation { return FALSE; } - return (MB_ENABLED === TRUE) - ? (mb_strlen($str) === (int) $val) - : (strlen($str) === (int) $val); + return (mb_strlen($str) === (int) $val); } // -------------------------------------------------------------------- @@ -1595,7 +1586,6 @@ class CI_Form_validation { public function reset_validation() { $this->_field_data = array(); - $this->_config_rules = array(); $this->_error_array = array(); $this->_error_messages = array(); $this->error_string = ''; diff --git a/system/libraries/Ftp.php b/system/libraries/Ftp.php index af45bb55f..2d345c294 100644 --- a/system/libraries/Ftp.php +++ b/system/libraries/Ftp.php @@ -466,7 +466,7 @@ class CI_FTP { /** * Delete a folder and recursively delete everything (including sub-folders) - * containted within it. + * contained within it. * * @param string $filepath * @return bool @@ -490,7 +490,7 @@ class CI_FTP { // so we'll recursively call delete_dir() if ( ! preg_match('#/\.\.?$#', $list[$i]) && ! @ftp_delete($this->conn_id, $list[$i])) { - $this->delete_dir($list[$i]); + $this->delete_dir($filepath.$list[$i]); } } } diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php index e056654bb..e813efd89 100644 --- a/system/libraries/Image_lib.php +++ b/system/libraries/Image_lib.php @@ -779,7 +779,7 @@ class CI_Image_lib { $this->y_axis = 0; } - // Create the image handle + // Create the image handle if ( ! ($src_img = $this->image_create_gd())) { return FALSE; @@ -845,7 +845,7 @@ class CI_Image_lib { */ public function image_process_imagemagick($action = 'resize') { - // Do we have a vaild library path? + // Do we have a vaild library path? if ($this->library_path === '') { $this->set_error('imglib_libpath_invalid'); @@ -1010,7 +1010,7 @@ class CI_Image_lib { // going to have to figure out how to determine the color // of the alpha channel in a future release. - $white = imagecolorallocate($src_img, 255, 255, 255); + $white = imagecolorallocate($src_img, 255, 255, 255); // Rotate it! $dst_img = imagerotate($src_img, $this->rotation_angle, $white); @@ -1055,8 +1055,11 @@ class CI_Image_lib { if ($this->rotation_angle === 'hor') { - for ($i = 0; $i < $height; $i++, $left = 0, $right = $width-1) + for ($i = 0; $i < $height; $i++) { + $left = 0; + $right = $width - 1; + while ($left < $right) { $cl = imagecolorat($src_img, $left, $i); @@ -1072,18 +1075,21 @@ class CI_Image_lib { } else { - for ($i = 0; $i < $width; $i++, $top = 0, $bot = $height-1) + for ($i = 0; $i < $width; $i++) { - while ($top < $bot) + $top = 0; + $bottom = $height - 1; + + while ($top < $bottom) { $ct = imagecolorat($src_img, $i, $top); - $cb = imagecolorat($src_img, $i, $bot); + $cb = imagecolorat($src_img, $i, $bottom); imagesetpixel($src_img, $i, $top, $cb); - imagesetpixel($src_img, $i, $bot, $ct); + imagesetpixel($src_img, $i, $bottom, $ct); $top++; - $bot--; + $bottom--; } } } @@ -1189,7 +1195,7 @@ class CI_Image_lib { $x_axis += $this->orig_width - $wm_width; } - // Build the finalized image + // Build the finalized image if ($wm_img_type === 3 && function_exists('imagealphablending')) { @imagealphablending($src_img, TRUE); @@ -1327,7 +1333,7 @@ class CI_Image_lib { { $y_axis += $this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight / 2); } - + // Set horizontal alignment if ($this->wm_hor_alignment === 'R') { @@ -1337,13 +1343,13 @@ class CI_Image_lib { { $x_axis += floor(($this->orig_width - ($fontwidth * strlen($this->wm_text))) / 2); } - + if ($this->wm_use_drop_shadow) { // Offset from text $x_shad = $x_axis + $this->wm_shadow_distance; $y_shad = $y_axis + $this->wm_shadow_distance; - + /* Set RGB values for shadow * * First character is #, so we don't really need it. @@ -1352,7 +1358,7 @@ class CI_Image_lib { */ $drp_color = str_split(substr($this->wm_shadow_color, 1, 6), 2); $drp_color = imagecolorclosest($src_img, hexdec($drp_color[0]), hexdec($drp_color[1]), hexdec($drp_color[2])); - + // Add the shadow to the source image if ($this->wm_use_truetype) { @@ -1363,7 +1369,7 @@ class CI_Image_lib { imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color); } } - + /* Set RGB values for text * * First character is #, so we don't really need it. @@ -1382,7 +1388,7 @@ class CI_Image_lib { { imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color); } - + // We can preserve transparency for PNG images if ($this->image_type === 3) { @@ -1431,7 +1437,7 @@ class CI_Image_lib { switch ($image_type) { - case 1 : + case 1: if ( ! function_exists('imagecreatefromgif')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported')); @@ -1439,7 +1445,7 @@ class CI_Image_lib { } return imagecreatefromgif($path); - case 2 : + case 2: if ( ! function_exists('imagecreatefromjpeg')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported')); @@ -1447,7 +1453,7 @@ class CI_Image_lib { } return imagecreatefromjpeg($path); - case 3 : + case 3: if ( ! function_exists('imagecreatefrompng')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported')); diff --git a/system/libraries/Javascript/Jquery.php b/system/libraries/Javascript/Jquery.php index 25acceef7..11f2d2361 100644 --- a/system/libraries/Javascript/Jquery.php +++ b/system/libraries/Javascript/Jquery.php @@ -84,7 +84,7 @@ class CI_Jquery extends CI_Javascript { public $jquery_table_sorter_active = FALSE; /** - * JQuery table sorder pager active + * JQuery table sorter pager active * * @var bool */ diff --git a/system/libraries/Javascript/index.html b/system/libraries/Javascript/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/Javascript/index.html +++ b/system/libraries/Javascript/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php index ae36a3b45..45a3cbbce 100644 --- a/system/libraries/Migration.php +++ b/system/libraries/Migration.php @@ -191,7 +191,7 @@ class CI_Migration { * choice * * @param string $target_version Target schema version - * @return mixed TRUE if already latest, FALSE if failed, string if upgraded + * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure */ public function version($target_version) { @@ -294,7 +294,7 @@ class CI_Migration { /** * Sets the schema to the latest migration * - * @return mixed TRUE if already latest, FALSE if failed, string if upgraded + * @return mixed Current version string on success, FALSE on failure */ public function latest() { @@ -318,7 +318,7 @@ class CI_Migration { /** * Sets the schema to the migration version set in config * - * @return mixed TRUE if already current, FALSE if failed, string if upgraded + * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure */ public function current() { diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php index d63f61df6..4d18998b9 100644 --- a/system/libraries/Pagination.php +++ b/system/libraries/Pagination.php @@ -353,7 +353,8 @@ class CI_Pagination { */ public function initialize(array $params = array()) { - if (isset($params['attributes']) && is_array($params['attributes'])) + isset($params['attributes']) OR $params['attributes'] = array(); + if (is_array($params['attributes'])) { $this->_parse_attributes($params['attributes']); unset($params['attributes']); @@ -570,7 +571,7 @@ class CI_Pagination { { $i = ($this->use_page_numbers) ? $uri_page_number - 1 : $uri_page_number - $this->per_page; - $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, ($this->cur_page - 1)); if ($i === $base_page) { @@ -591,11 +592,11 @@ class CI_Pagination { if ($this->display_pages !== FALSE) { // Write the digit links - for ($loop = $start -1; $loop <= $end; $loop++) + for ($loop = $start - 1; $loop <= $end; $loop++) { $i = ($this->use_page_numbers) ? $loop : ($loop * $this->per_page) - $this->per_page; - $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $loop); if ($i >= $base_page) { @@ -613,7 +614,7 @@ class CI_Pagination { else { $append = $this->prefix.$i.$this->suffix; - $output .= $this->num_tag_open.'<a href="'.$base_url.$append.'"'.$attributes.$this->_attr_rel('start').'>' + $output .= $this->num_tag_open.'<a href="'.$base_url.$append.'"'.$attributes.'>' .$loop.'</a>'.$this->num_tag_close; } } @@ -625,7 +626,7 @@ class CI_Pagination { { $i = ($this->use_page_numbers) ? $this->cur_page + 1 : $this->cur_page * $this->per_page; - $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $this->cur_page + 1); $output .= $this->next_tag_open.'<a href="'.$base_url.$this->prefix.$i.$this->suffix.'"'.$attributes .$this->_attr_rel('next').'>'.$this->next_link.'</a>'.$this->next_tag_close; @@ -636,7 +637,7 @@ class CI_Pagination { { $i = ($this->use_page_numbers) ? $num_pages : ($num_pages * $this->per_page) - $this->per_page; - $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, (int) $i); + $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $num_pages); $output .= $this->last_tag_open.'<a href="'.$base_url.$this->prefix.$i.$this->suffix.'"'.$attributes.'>' .$this->last_link.'</a>'.$this->last_tag_close; @@ -644,7 +645,7 @@ class CI_Pagination { // Kill double slashes. Note: Sometimes we can end up with a double slash // in the penultimate link so we'll kill all double slashes. - $output = preg_replace('#([^:])//+#', '\\1/', $output); + $output = preg_replace('#([^:"])//+#', '\\1/', $output); // Add the wrapper HTML if exists return $this->full_tag_open.$output.$this->full_tag_close; diff --git a/system/libraries/Profiler.php b/system/libraries/Profiler.php index f35d23faf..1e464d8b0 100644 --- a/system/libraries/Profiler.php +++ b/system/libraries/Profiler.php @@ -100,12 +100,6 @@ class CI_Profiler { $this->CI =& get_instance(); $this->CI->load->language('profiler'); - if (isset($config['query_toggle_count'])) - { - $this->_query_toggle_count = (int) $config['query_toggle_count']; - unset($config['query_toggle_count']); - } - // default all sections to display foreach ($this->_available_sections as $section) { diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php index ba1919b44..05a470d86 100644 --- a/system/libraries/Session/Session.php +++ b/system/libraries/Session/Session.php @@ -94,10 +94,7 @@ class CI_Session { $this->_driver = 'database'; } - if (($class = $this->_ci_load_classes($this->_driver)) === FALSE) - { - return; - } + $class = $this->_ci_load_classes($this->_driver); // Configuration ... $this->_configure($params); @@ -153,7 +150,7 @@ class CI_Session { } elseif ($_SESSION['__ci_last_regenerate'] < (time() - $regenerate_time)) { - $this->sess_regenerate(FALSE); + $this->sess_regenerate((bool) config_item('sess_regenerate_destroy')); } } // Another work-around ... PHP doesn't seem to send the session cookie @@ -230,8 +227,7 @@ class CI_Session { if ( ! class_exists('CI_'.$class, FALSE) && ! class_exists($class, FALSE)) { - log_message('error', "Session: Configured driver '".$driver."' was not found. Aborting."); - return FALSE; + throw new UnexpectedValueException("Session: Configured driver '".$driver."' was not found. Aborting."); } } @@ -314,7 +310,7 @@ class CI_Session { $this->_config = $params; // Security is king - ini_set('session.use_trans_id', 0); + ini_set('session.use_trans_sid', 0); ini_set('session.use_strict_mode', 1); ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); @@ -799,7 +795,7 @@ class CI_Session { /** * Set flashdata * - * Legacy CI_Session compatibiliy method + * Legacy CI_Session compatibility method * * @param mixed $data Session data key or an associative array * @param mixed $value Value to store @@ -873,7 +869,7 @@ class CI_Session { public function set_tempdata($data, $value = NULL, $ttl = 300) { $this->set_userdata($data, $value); - $this->mark_as_temp($data, $ttl); + $this->mark_as_temp(is_array($data) ? array_keys($data) : $data, $ttl); } // ------------------------------------------------------------------------ diff --git a/system/libraries/Session/drivers/Session_database_driver.php b/system/libraries/Session/drivers/Session_database_driver.php index 20cec00fd..72b39d12d 100644 --- a/system/libraries/Session/drivers/Session_database_driver.php +++ b/system/libraries/Session/drivers/Session_database_driver.php @@ -93,6 +93,10 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan { throw new Exception('Configured database connection is persistent. Aborting.'); } + elseif ($this->_db->cache_on) + { + throw new Exception('Configured database connection has cache enabled. Aborting.'); + } $db_driver = $this->_db->dbdriver.(empty($this->_db->subdriver) ? '' : '_'.$this->_db->subdriver); if (strpos($db_driver, 'mysql') !== FALSE) @@ -122,7 +126,7 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan public function open($save_path, $name) { return empty($this->_db->conn_id) - ? ( ! $this->_db->autoinit && $this->_db->db_connect()) + ? (bool) $this->_db->db_connect() : TRUE; } @@ -155,6 +159,10 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan if (($result = $this->_db->get()->row()) === NULL) { + // PHP7 will reuse the same SessionHandler object after + // ID regeneration, so we need to explicitly set this to + // FALSE instead of relying on the default ... + $this->_row_exists = FALSE; $this->_fingerprint = md5(''); return ''; } @@ -319,7 +327,7 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan if ($this->_platform === 'mysql') { $arg = $session_id.($this->_config['match_ip'] ? '_'.$_SERVER['REMOTE_ADDR'] : ''); - if ($this->_db->query("SELECT GET_LOCK('".$arg."', 10) AS ci_session_lock")->row()->ci_session_lock) + if ($this->_db->query("SELECT GET_LOCK('".$arg."', 300) AS ci_session_lock")->row()->ci_session_lock) { $this->_lock = $arg; return TRUE; diff --git a/system/libraries/Session/drivers/Session_files_driver.php b/system/libraries/Session/drivers/Session_files_driver.php index d3ef34acd..45da91c46 100644 --- a/system/libraries/Session/drivers/Session_files_driver.php +++ b/system/libraries/Session/drivers/Session_files_driver.php @@ -107,7 +107,7 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle * Sanitizes the save_path directory. * * @param string $save_path Path to session files' directory - * @param string $name Session cookie name, unused + * @param string $name Session cookie name * @return bool */ public function open($save_path, $name) @@ -299,7 +299,9 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle { if ($this->close()) { - return unlink($this->_file_path.$session_id) && $this->_cookie_destroy(); + return file_exists($this->_file_path.$session_id) + ? (unlink($this->_file_path.$session_id) && $this->_cookie_destroy()) + : TRUE; } elseif ($this->_file_path !== NULL) { @@ -324,7 +326,7 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle */ public function gc($maxlifetime) { - if ( ! is_dir($this->_config['save_path']) OR ($files = scandir($this->_config['save_path'])) === FALSE) + if ( ! is_dir($this->_config['save_path']) OR ($directory = opendir($this->_config['save_path'])) === FALSE) { log_message('debug', "Session: Garbage collector couldn't list files under directory '".$this->_config['save_path']."'."); return FALSE; @@ -332,10 +334,16 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle $ts = time() - $maxlifetime; - foreach ($files as $file) + $pattern = sprintf( + '/^%s[0-9a-f]{%d}$/', + preg_quote($this->_config['cookie_name'], '/'), + ($this->_config['match_ip'] === TRUE ? 72 : 40) + ); + + while (($file = readdir($directory)) !== FALSE) { // If the filename doesn't match this pattern, it's either not a session file or is not ours - if ( ! preg_match('/(?:[0-9a-f]{32})?[0-9a-f]{40}$/i', $file) + if ( ! preg_match($pattern, $file) OR ! is_file($this->_config['save_path'].DIRECTORY_SEPARATOR.$file) OR ($mtime = filemtime($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)) === FALSE OR $mtime > $ts) @@ -346,6 +354,8 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle unlink($this->_config['save_path'].DIRECTORY_SEPARATOR.$file); } + closedir($directory); + return TRUE; } diff --git a/system/libraries/Session/drivers/Session_memcached_driver.php b/system/libraries/Session/drivers/Session_memcached_driver.php index 600b8ca66..97b860588 100644 --- a/system/libraries/Session/drivers/Session_memcached_driver.php +++ b/system/libraries/Session/drivers/Session_memcached_driver.php @@ -204,7 +204,7 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa if (isset($this->_lock_key)) { - $this->_memcached->replace($this->_lock_key, time(), 5); + $this->_memcached->replace($this->_lock_key, time(), 300); if ($this->_fingerprint !== ($fingerprint = md5($session_data))) { if ($this->_memcached->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration'])) @@ -299,34 +299,21 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa { if (isset($this->_lock_key)) { - return $this->_memcached->replace($this->_lock_key, time(), 5); + return $this->_memcached->replace($this->_lock_key, time(), 300); } + // 30 attempts to obtain a lock, in case another request already has it $lock_key = $this->_key_prefix.$session_id.':lock'; - if ( ! ($ts = $this->_memcached->get($lock_key))) - { - if ( ! $this->_memcached->set($lock_key, TRUE, 5)) - { - log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); - return FALSE; - } - - $this->_lock_key = $lock_key; - $this->_lock = TRUE; - return TRUE; - } - - // Another process has the lock, we'll try to wait for it to free itself ... $attempt = 0; - while ($attempt++ < 5) + do { - usleep(((time() - $ts) * 1000000) - 20000); - if (($ts = $this->_memcached->get($lock_key)) < time()) + if ($this->_memcached->get($lock_key)) { + sleep(1); continue; } - if ( ! $this->_memcached->set($lock_key, time(), 5)) + if ( ! $this->_memcached->set($lock_key, time(), 300)) { log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); return FALSE; @@ -335,10 +322,11 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa $this->_lock_key = $lock_key; break; } + while (++$attempt < 30); - if ($attempt === 5) + if ($attempt === 30) { - log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 5 attempts, aborting.'); + log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.'); return FALSE; } @@ -361,7 +349,7 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa { if ( ! $this->_memcached->delete($this->_lock_key) && $this->_memcached->getResultCode() !== Memcached::RES_NOTFOUND) { - log_message('error', 'Session: Error while trying to free lock for '.$this->_key_prefix.$session_id); + log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key); return FALSE; } diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php index c3c75b3b6..b098cc441 100644 --- a/system/libraries/Session/drivers/Session_redis_driver.php +++ b/system/libraries/Session/drivers/Session_redis_driver.php @@ -205,7 +205,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle if (isset($this->_lock_key)) { - $this->_redis->setTimeout($this->_lock_key, 5); + $this->_redis->setTimeout($this->_lock_key, 300); if ($this->_fingerprint !== ($fingerprint = md5($session_data))) { if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration'])) @@ -272,7 +272,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle { if (isset($this->_redis, $this->_lock_key)) { - if ($this->_redis->delete($this->_key_prefix.$session_id) !== 1) + if (($result = $this->_redis->delete($this->_key_prefix.$session_id)) !== 1) { log_message('debug', 'Session: Redis::delete() expected to return 1, got '.var_export($result, TRUE).' instead.'); } @@ -313,40 +313,21 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle { if (isset($this->_lock_key)) { - return $this->_redis->setTimeout($this->_lock_key, 5); + return $this->_redis->setTimeout($this->_lock_key, 300); } + // 30 attempts to obtain a lock, in case another request already has it $lock_key = $this->_key_prefix.$session_id.':lock'; - if (($ttl = $this->_redis->ttl($lock_key)) < 1) - { - if ( ! $this->_redis->setex($lock_key, 5, time())) - { - log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); - return FALSE; - } - - $this->_lock_key = $lock_key; - - if ($ttl === -1) - { - log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.'); - } - - $this->_lock = TRUE; - return TRUE; - } - - // Another process has the lock, we'll try to wait for it to free itself ... $attempt = 0; - while ($attempt++ < 5) + do { - usleep(($ttl * 1000000) - 20000); if (($ttl = $this->_redis->ttl($lock_key)) > 0) { + sleep(1); continue; } - if ( ! $this->_redis->setex($lock_key, 5, time())) + if ( ! $this->_redis->setex($lock_key, 300, time())) { log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); return FALSE; @@ -355,12 +336,17 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle $this->_lock_key = $lock_key; break; } + while (++$attempt < 30); - if ($attempt === 5) + if ($attempt === 30) { - log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 5 attempts, aborting.'); + log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.'); return FALSE; } + elseif ($ttl === -1) + { + log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.'); + } $this->_lock = TRUE; return TRUE; @@ -381,7 +367,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle { if ( ! $this->_redis->delete($this->_lock_key)) { - log_message('error', 'Session: Error while trying to free lock for '.$this->_key_prefix.$session_id); + log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key); return FALSE; } diff --git a/system/libraries/Session/drivers/index.html b/system/libraries/Session/drivers/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/Session/drivers/index.html +++ b/system/libraries/Session/drivers/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Session/index.html b/system/libraries/Session/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/Session/index.html +++ b/system/libraries/Session/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/system/libraries/Unit_test.php b/system/libraries/Unit_test.php index 7b744adc6..3f986f3e8 100644 --- a/system/libraries/Unit_test.php +++ b/system/libraries/Unit_test.php @@ -55,14 +55,14 @@ class CI_Unit_test { * * @var bool */ - public $active = TRUE; + public $active = TRUE; /** * Test results * * @var array */ - public $results = array(); + public $results = array(); /** * Strict comparison flag @@ -71,21 +71,21 @@ class CI_Unit_test { * * @var bool */ - public $strict = FALSE; + public $strict = FALSE; /** * Template * * @var string */ - protected $_template = NULL; + protected $_template = NULL; /** * Template rows * * @var string */ - protected $_template_rows = NULL; + protected $_template_rows = NULL; /** * List of visible test items @@ -93,13 +93,13 @@ class CI_Unit_test { * @var array */ protected $_test_items_visible = array( - 'test_name', - 'test_datatype', - 'res_datatype', - 'result', - 'file', - 'line', - 'notes' + 'test_name', + 'test_datatype', + 'res_datatype', + 'result', + 'file', + 'line', + 'notes' ); // -------------------------------------------------------------------- @@ -152,7 +152,7 @@ class CI_Unit_test { return FALSE; } - if (in_array($expected, array('is_object', 'is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null'), TRUE)) + if (in_array($expected, array('is_object', 'is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null', 'is_resource'), TRUE)) { $expected = str_replace('is_double', 'is_float', $expected); $result = $expected($test); @@ -167,14 +167,14 @@ class CI_Unit_test { $back = $this->_backtrace(); $report = array ( - 'test_name' => $test_name, - 'test_datatype' => gettype($test), - 'res_datatype' => $extype, - 'result' => ($result === TRUE) ? 'passed' : 'failed', - 'file' => $back['file'], - 'line' => $back['line'], - 'notes' => $notes - ); + 'test_name' => $test_name, + 'test_datatype' => gettype($test), + 'res_datatype' => $extype, + 'result' => ($result === TRUE) ? 'passed' : 'failed', + 'file' => $back['file'], + 'line' => $back['line'], + 'notes' => $notes + ); $this->results[] = $report; @@ -291,10 +291,12 @@ class CI_Unit_test { { continue; } - - if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val), FALSE))) + elseif (in_array($key, array('test_name', 'test_datatype', 'test_res_datatype', 'result'), TRUE)) { - $val = $line; + if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val), FALSE))) + { + $val = $line; + } } $temp[$CI->lang->line('ut_'.$key, FALSE)] = $val; @@ -334,9 +336,9 @@ class CI_Unit_test { { $back = debug_backtrace(); return array( - 'file' => (isset($back[1]['file']) ? $back[1]['file'] : ''), - 'line' => (isset($back[1]['line']) ? $back[1]['line'] : '') - ); + 'file' => (isset($back[1]['file']) ? $back[1]['file'] : ''), + 'line' => (isset($back[1]['line']) ? $back[1]['line'] : '') + ); } // -------------------------------------------------------------------- diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php index f5534a7f7..8a2dec76a 100644 --- a/system/libraries/Upload.php +++ b/system/libraries/Upload.php @@ -397,7 +397,7 @@ class CI_Upload { if ( ! isset($_file)) { - $this->set_error('upload_no_file_selected'); + $this->set_error('upload_no_file_selected', 'debug'); return FALSE; } @@ -416,28 +416,28 @@ class CI_Upload { switch ($error) { case UPLOAD_ERR_INI_SIZE: - $this->set_error('upload_file_exceeds_limit'); + $this->set_error('upload_file_exceeds_limit', 'info'); break; case UPLOAD_ERR_FORM_SIZE: - $this->set_error('upload_file_exceeds_form_limit'); + $this->set_error('upload_file_exceeds_form_limit', 'info'); break; case UPLOAD_ERR_PARTIAL: - $this->set_error('upload_file_partial'); + $this->set_error('upload_file_partial', 'debug'); break; case UPLOAD_ERR_NO_FILE: - $this->set_error('upload_no_file_selected'); + $this->set_error('upload_no_file_selected', 'debug'); break; case UPLOAD_ERR_NO_TMP_DIR: - $this->set_error('upload_no_temp_directory'); + $this->set_error('upload_no_temp_directory', 'error'); break; case UPLOAD_ERR_CANT_WRITE: - $this->set_error('upload_unable_to_write_file'); + $this->set_error('upload_unable_to_write_file', 'error'); break; case UPLOAD_ERR_EXTENSION: - $this->set_error('upload_stopped_by_extension'); + $this->set_error('upload_stopped_by_extension', 'debug'); break; default: - $this->set_error('upload_no_file_selected'); + $this->set_error('upload_no_file_selected', 'debug'); break; } @@ -463,7 +463,7 @@ class CI_Upload { // Is the file type allowed to be uploaded? if ( ! $this->is_allowed_filetype()) { - $this->set_error('upload_invalid_filetype'); + $this->set_error('upload_invalid_filetype', 'debug'); return FALSE; } @@ -485,7 +485,7 @@ class CI_Upload { if ( ! $this->is_allowed_filetype(TRUE)) { - $this->set_error('upload_invalid_filetype'); + $this->set_error('upload_invalid_filetype', 'debug'); return FALSE; } } @@ -499,7 +499,7 @@ class CI_Upload { // Is the file size within the allowed maximum? if ( ! $this->is_allowed_filesize()) { - $this->set_error('upload_invalid_filesize'); + $this->set_error('upload_invalid_filesize', 'info'); return FALSE; } @@ -507,7 +507,7 @@ class CI_Upload { // Note: This can fail if the server has an open_basedir restriction. if ( ! $this->is_allowed_dimensions()) { - $this->set_error('upload_invalid_dimensions'); + $this->set_error('upload_invalid_dimensions', 'info'); return FALSE; } @@ -533,15 +533,9 @@ class CI_Upload { * If it returns false there was a problem. */ $this->orig_name = $this->file_name; - - if ($this->overwrite === FALSE) + if (FALSE === ($this->file_name = $this->set_filename($this->upload_path, $this->file_name))) { - $this->file_name = $this->set_filename($this->upload_path, $this->file_name); - - if ($this->file_name === FALSE) - { - return FALSE; - } + return FALSE; } /* @@ -552,7 +546,7 @@ class CI_Upload { */ if ($this->xss_clean && $this->do_xss_clean() === FALSE) { - $this->set_error('upload_unable_to_write_file'); + $this->set_error('upload_unable_to_write_file', 'error'); return FALSE; } @@ -567,7 +561,7 @@ class CI_Upload { { if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name)) { - $this->set_error('upload_destination_error'); + $this->set_error('upload_destination_error', 'error'); return FALSE; } } @@ -656,7 +650,7 @@ class CI_Upload { $filename = md5(uniqid(mt_rand())).$this->file_ext; } - if ( ! file_exists($path.$filename)) + if ($this->overwrite === TRUE OR ! file_exists($path.$filename)) { return $filename; } @@ -675,7 +669,7 @@ class CI_Upload { if ($new_filename === '') { - $this->set_error('upload_bad_filename'); + $this->set_error('upload_bad_filename', 'debug'); return FALSE; } else @@ -701,6 +695,22 @@ class CI_Upload { // -------------------------------------------------------------------- /** + * Set Maximum File Size + * + * An internal alias to set_max_filesize() to help with configuration + * as initialize() will look for a set_<property_name>() method ... + * + * @param int $n + * @return CI_Upload + */ + protected function set_max_size($n) + { + return $this->set_max_filesize($n); + } + + // -------------------------------------------------------------------- + + /** * Set Maximum File Name Length * * @param int $n @@ -875,7 +885,7 @@ class CI_Upload { if (empty($this->allowed_types) OR ! is_array($this->allowed_types)) { - $this->set_error('upload_no_file_types'); + $this->set_error('upload_no_file_types', 'debug'); return FALSE; } @@ -974,7 +984,7 @@ class CI_Upload { { if ($this->upload_path === '') { - $this->set_error('upload_no_filepath'); + $this->set_error('upload_no_filepath', 'error'); return FALSE; } @@ -985,13 +995,13 @@ class CI_Upload { if ( ! is_dir($this->upload_path)) { - $this->set_error('upload_no_filepath'); + $this->set_error('upload_no_filepath', 'error'); return FALSE; } if ( ! is_really_writable($this->upload_path)) { - $this->set_error('upload_not_writable'); + $this->set_error('upload_not_writable', 'error'); return FALSE; } @@ -1013,7 +1023,7 @@ class CI_Upload { if (count($x) === 1) { - return ''; + return ''; } $ext = ($this->file_ext_tolower) ? strtolower(end($x)) : end($x); @@ -1121,17 +1131,16 @@ class CI_Upload { * @param string $msg * @return CI_Upload */ - public function set_error($msg) + public function set_error($msg, $log_level = 'error') { $this->_CI->lang->load('upload'); is_array($msg) OR $msg = array($msg); - foreach ($msg as $val) { $msg = ($this->_CI->lang->line($val) === FALSE) ? $val : $this->_CI->lang->line($val); $this->error_msg[] = $msg; - log_message('error', $msg); + log_message($log_level, $msg); } return $this; diff --git a/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php index 8fbc18f04..55555f56f 100644 --- a/system/libraries/Xmlrpc.php +++ b/system/libraries/Xmlrpc.php @@ -735,12 +735,32 @@ class XML_RPC_Client extends CI_Xmlrpc .'Content-Length: '.strlen($msg->payload).$r.$r .$msg->payload; - for ($written = 0, $length = strlen($op); $written < $length; $written += $result) + for ($written = $timestamp = 0, $length = strlen($op); $written < $length; $written += $result) { if (($result = fwrite($fp, substr($op, $written))) === FALSE) { break; } + // See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951 + elseif ($result === 0) + { + if ($timestamp === 0) + { + $timestamp = time(); + } + elseif ($timestamp < (time() - $this->timeout)) + { + $result = FALSE; + break; + } + + usleep(250000); + continue; + } + else + { + $timestamp = 0; + } } if ($result === FALSE) diff --git a/system/libraries/Xmlrpcs.php b/system/libraries/Xmlrpcs.php index af7041337..00d1feca6 100644 --- a/system/libraries/Xmlrpcs.php +++ b/system/libraries/Xmlrpcs.php @@ -223,7 +223,7 @@ class CI_Xmlrpcs extends CI_Xmlrpc { $CI =& get_instance(); if ($CI->input->method() === 'post') { - $data = http_build_query($CI->input->input_stream(NULL, FALSE)); + $data = $CI->input->raw_input_stream; } } @@ -233,14 +233,15 @@ class CI_Xmlrpcs extends CI_Xmlrpc { $parser = xml_parser_create($this->xmlrpc_defencoding); $parser_object = new XML_RPC_Message('filler'); - - $parser_object->xh[$parser] = array( - 'isf' => 0, - 'isf_reason' => '', - 'params' => array(), - 'stack' => array(), - 'valuestack' => array(), - 'method' => '' + $pname = (string) $parser; + + $parser_object->xh[$pname] = array( + 'isf' => 0, + 'isf_reason' => '', + 'params' => array(), + 'stack' => array(), + 'valuestack' => array(), + 'method' => '' ); xml_set_object($parser, $parser_object); @@ -263,7 +264,7 @@ class CI_Xmlrpcs extends CI_Xmlrpc { xml_get_current_line_number($parser))); xml_parser_free($parser); } - elseif ($parser_object->xh[$parser]['isf']) + elseif ($parser_object->xh[$pname]['isf']) { return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']); } @@ -271,17 +272,17 @@ class CI_Xmlrpcs extends CI_Xmlrpc { { xml_parser_free($parser); - $m = new XML_RPC_Message($parser_object->xh[$parser]['method']); + $m = new XML_RPC_Message($parser_object->xh[$pname]['method']); $plist = ''; - for ($i = 0, $c = count($parser_object->xh[$parser]['params']); $i < $c; $i++) + for ($i = 0, $c = count($parser_object->xh[$pname]['params']); $i < $c; $i++) { if ($this->debug === TRUE) { - $plist .= $i.' - '.print_r(get_object_vars($parser_object->xh[$parser]['params'][$i]), TRUE).";\n"; + $plist .= $i.' - '.print_r(get_object_vars($parser_object->xh[$pname]['params'][$i]), TRUE).";\n"; } - $m->addParam($parser_object->xh[$parser]['params'][$i]); + $m->addParam($parser_object->xh[$pname]['params'][$i]); } if ($this->debug === TRUE) diff --git a/system/libraries/Zip.php b/system/libraries/Zip.php index f2f17148b..3e98ac568 100644 --- a/system/libraries/Zip.php +++ b/system/libraries/Zip.php @@ -352,7 +352,7 @@ class CI_Zip { // Set the original directory root for child dir's to use as relative if ($root_path === NULL) { - $root_path = dirname($path).DIRECTORY_SEPARATOR; + $root_path = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, dirname($path)).DIRECTORY_SEPARATOR; } while (FALSE !== ($file = readdir($fp))) diff --git a/system/libraries/index.html b/system/libraries/index.html index c942a79ce..b702fbc39 100644 --- a/system/libraries/index.html +++ b/system/libraries/index.html @@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <title>403 Forbidden</title> @@ -7,4 +8,4 @@ <p>Directory access is forbidden.</p> </body> -</html>
\ No newline at end of file +</html> |