diff options
58 files changed, 669 insertions, 428 deletions
diff --git a/.travis.yml b/.travis.yml index 1e0ee65db..4680d2ac6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,10 @@ env: sudo: false before_script: +<<<<<<< HEAD - sh -c "composer install --dev --no-progress" +======= +>>>>>>> 3.1-stable - sh -c "if [ '$DB' = 'pgsql' ] || [ '$DB' = 'pdo/pgsql' ]; then psql -c 'DROP DATABASE IF EXISTS ci_test;' -U postgres; fi" - sh -c "if [ '$DB' = 'pgsql' ] || [ '$DB' = 'pdo/pgsql' ]; then psql -c 'create database ci_test;' -U postgres; fi" - sh -c "if [ '$DB' = 'mysql' ] || [ '$DB' = 'mysqli' ] || [ '$DB' = 'pdo/mysql' ]; then mysql -e 'create database IF NOT EXISTS ci_test;'; fi" @@ -41,4 +44,5 @@ branches: only: - develop - 3.0-stable + - 3.1-stable - /^feature\/.+$/ diff --git a/readme.rst b/readme.rst index 7a376322d..f5d737028 100644 --- a/readme.rst +++ b/readme.rst @@ -31,7 +31,7 @@ Server Requirements PHP version 5.6 or newer is recommended. -It should work on 5.2.4 as well, but we strongly advise you NOT to run +It should work on 5.3.7 as well, but we strongly advise you NOT to run such old versions of PHP, because of potential security and performance issues, as well as missing features. diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php index d9ac7efe3..5e60a8d40 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.1.0-dev'); + define('CI_VERSION', '3.2.0-dev'); /* * ------------------------------------------------------ diff --git a/system/core/Config.php b/system/core/Config.php index 5c6ba2a4d..e74751639 100644 --- a/system/core/Config.php +++ b/system/core/Config.php @@ -319,7 +319,7 @@ class CI_Config { } } - return $base_url.ltrim($this->_uri_string($uri), '/'); + return $base_url.$this->_uri_string($uri); } // ------------------------------------------------------------- @@ -337,11 +337,8 @@ class CI_Config { { if ($this->item('enable_query_strings') === FALSE) { - if (is_array($uri)) - { - $uri = implode('/', $uri); - } - return trim($uri, '/'); + is_array($uri) && $uri = implode('/', $uri); + return ltrim($uri, '/'); } elseif (is_array($uri)) { diff --git a/system/core/Input.php b/system/core/Input.php index 59b39620c..f6397e35b 100644 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -519,9 +519,9 @@ class CI_Input { if ($separator === ':') { $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr)); - for ($i = 0; $i < 8; $i++) + for ($j = 0; $j < 8; $j++) { - $netaddr[$i] = intval($netaddr[$i], 16); + $netaddr[$i] = intval($netaddr[$j], 16); } } else @@ -760,30 +760,32 @@ class CI_Input { // If header is already defined, return it immediately if ( ! empty($this->headers)) { - return $this->headers; + return $this->_fetch_from_array($this->headers, NULL, $xss_clean); } // In Apache, you can simply call apache_request_headers() if (function_exists('apache_request_headers')) { - return $this->headers = apache_request_headers(); + $this->headers = apache_request_headers(); } - - $this->headers['Content-Type'] = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : @getenv('CONTENT_TYPE'); - - foreach ($_SERVER as $key => $val) + else { - if (sscanf($key, 'HTTP_%s', $header) === 1) + isset($_SERVER['CONTENT_TYPE']) && $this->headers['Content-Type'] = $_SERVER['CONTENT_TYPE']; + + foreach ($_SERVER as $key => $val) { - // take SOME_HEADER and turn it into Some-Header - $header = str_replace('_', ' ', strtolower($header)); - $header = str_replace(' ', '-', ucwords($header)); + if (sscanf($key, 'HTTP_%s', $header) === 1) + { + // take SOME_HEADER and turn it into Some-Header + $header = str_replace('_', ' ', strtolower($header)); + $header = str_replace(' ', '-', ucwords($header)); - $this->headers[$header] = $this->_fetch_from_array($_SERVER, $key, $xss_clean); + $this->headers[$header] = $_SERVER[$key]; + } } } - return $this->headers; + return $this->_fetch_from_array($this->headers, NULL, $xss_clean); } // -------------------------------------------------------------------- diff --git a/system/core/compat/password.php b/system/core/compat/password.php index f0c22c780..76dd2cf0a 100644 --- a/system/core/compat/password.php +++ b/system/core/compat/password.php @@ -116,13 +116,21 @@ if ( ! function_exists('password_hash')) } elseif ( ! isset($options['salt'])) { - if (defined('MCRYPT_DEV_URANDOM')) + if (function_exists('random_bytes')) { - $options['salt'] = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM); + try + { + $options['salt'] = random_bytes(16); + } + catch (Exception $e) + { + log_message('error', 'compat/password: Error while trying to use random_bytes(): '.$e->getMessage()); + return FALSE; + } } - elseif (function_exists('openssl_random_pseudo_bytes')) + elseif (defined('MCRYPT_DEV_URANDOM')) { - $options['salt'] = openssl_random_pseudo_bytes(16); + $options['salt'] = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM); } elseif (DIRECTORY_SEPARATOR === '/' && (is_readable($dev = '/dev/arandom') OR is_readable($dev = '/dev/urandom'))) { @@ -148,6 +156,16 @@ if ( ! function_exists('password_hash')) fclose($fp); } + elseif (function_exists('openssl_random_pseudo_bytes')) + { + $is_secure = NULL; + $options['salt'] = openssl_random_pseudo_bytes(16, $is_secure); + if ($is_secure !== TRUE) + { + log_message('error', 'compat/password: openssl_random_pseudo_bytes() set the $cryto_strong flag to FALSE'); + return FALSE; + } + } else { log_message('error', 'compat/password: No CSPRNG available.'); diff --git a/system/database/DB_query_builder.php b/system/database/DB_query_builder.php index c862d937d..713bf18f3 100644 --- a/system/database/DB_query_builder.php +++ b/system/database/DB_query_builder.php @@ -1395,7 +1395,7 @@ abstract class CI_DB_query_builder extends CI_DB_driver { $this->qb_orderby = NULL; } - $result = ($this->qb_distinct === TRUE) + $result = ($this->qb_distinct === TRUE OR ! empty($this->qb_groupby)) ? $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'))); @@ -1498,8 +1498,10 @@ abstract class CI_DB_query_builder extends CI_DB_driver { $affected_rows = 0; for ($i = 0, $total = count($this->qb_set); $i < $total; $i += $batch_size) { - $this->query($this->_insert_batch($this->protect_identifiers($table, TRUE, $escape, FALSE), $this->qb_keys, array_slice($this->qb_set, $i, $batch_size))); - $affected_rows += $this->affected_rows(); + if ($this->query($this->_insert_batch($this->protect_identifiers($table, TRUE, $escape, FALSE), $this->qb_keys, array_slice($this->qb_set, $i, $batch_size)))) + { + $affected_rows += $this->affected_rows(); + } } $this->_reset_write(); @@ -1913,8 +1915,11 @@ abstract class CI_DB_query_builder extends CI_DB_driver { $affected_rows = 0; for ($i = 0, $total = count($this->qb_set); $i < $total; $i += $batch_size) { - $this->query($this->_update_batch($this->protect_identifiers($table, TRUE, NULL, FALSE), array_slice($this->qb_set, $i, $batch_size), $this->protect_identifiers($index))); - $affected_rows += $this->affected_rows(); + if ($this->query($this->_update_batch($this->protect_identifiers($table, TRUE, NULL, FALSE), array_slice($this->qb_set, $i, $batch_size), $this->protect_identifiers($index)))) + { + $affected_rows += $this->affected_rows(); + } + $this->qb_where = array(); } diff --git a/system/database/drivers/cubrid/cubrid_forge.php b/system/database/drivers/cubrid/cubrid_forge.php index 8262beb6b..46a3b2185 100644 --- a/system/database/drivers/cubrid/cubrid_forge.php +++ b/system/database/drivers/cubrid/cubrid_forge.php @@ -178,6 +178,9 @@ class CI_DB_cubrid_forge extends CI_DB_forge { $attributes['TYPE'] = 'INTEGER'; $attributes['UNSIGNED'] = FALSE; return; + case 'LONGTEXT': + $attributes['TYPE'] = 'STRING'; + return; default: return; } } diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php index 72e31e00d..76f58564b 100644 --- a/system/database/drivers/mssql/mssql_driver.php +++ b/system/database/drivers/mssql/mssql_driver.php @@ -159,6 +159,7 @@ class CI_DB_mssql_driver extends CI_DB { if (mssql_select_db('['.$database.']', $this->conn_id)) { $this->database = $database; + $this->data_cache = array(); return TRUE; } diff --git a/system/database/drivers/mssql/mssql_forge.php b/system/database/drivers/mssql/mssql_forge.php index 24d1fc204..91b5794bc 100644 --- a/system/database/drivers/mssql/mssql_forge.php +++ b/system/database/drivers/mssql/mssql_forge.php @@ -113,6 +113,11 @@ class CI_DB_mssql_forge extends CI_DB_forge { */ protected function _attr_type(&$attributes) { + if (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE) + { + unset($attributes['CONSTRAINT']); + } + switch (strtoupper($attributes['TYPE'])) { case 'MEDIUMINT': diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php index d903406d1..70b4f68ba 100644 --- a/system/database/drivers/mysql/mysql_driver.php +++ b/system/database/drivers/mysql/mysql_driver.php @@ -220,6 +220,7 @@ class CI_DB_mysql_driver extends CI_DB { if (mysql_select_db($database, $this->conn_id)) { $this->database = $database; + $this->data_cache = array(); return TRUE; } diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php index 431bce50a..168533240 100644 --- a/system/database/drivers/mysqli/mysqli_driver.php +++ b/system/database/drivers/mysqli/mysqli_driver.php @@ -263,6 +263,7 @@ class CI_DB_mysqli_driver extends CI_DB { if ($this->conn_id->select_db($database)) { $this->database = $database; + $this->data_cache = array(); return TRUE; } diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php index 59f0e8456..df7e0848a 100644 --- a/system/database/drivers/oci8/oci8_driver.php +++ b/system/database/drivers/oci8/oci8_driver.php @@ -559,23 +559,29 @@ class CI_DB_oci8_driver extends CI_DB { */ public function error() { - /* oci_error() returns an array that already contains the - * 'code' and 'message' keys, so we can just return it. - */ + // oci_error() returns an array that already contains + // 'code' and 'message' keys, but it can return false + // if there was no error .... if (is_resource($this->curs_id)) { - return oci_error($this->curs_id); + $error = oci_error($this->curs_id); } elseif (is_resource($this->stmt_id)) { - return oci_error($this->stmt_id); + $error = oci_error($this->stmt_id); } elseif (is_resource($this->conn_id)) { - return oci_error($this->conn_id); + $error = oci_error($this->conn_id); + } + else + { + $error = oci_error(); } - return oci_error(); + return is_array($error) + ? $error + : array('code' => '', 'message' => ''); } // -------------------------------------------------------------------- diff --git a/system/database/drivers/oci8/oci8_forge.php b/system/database/drivers/oci8/oci8_forge.php index 989c7a8b7..23e025757 100644 --- a/system/database/drivers/oci8/oci8_forge.php +++ b/system/database/drivers/oci8/oci8_forge.php @@ -182,5 +182,4 @@ class CI_DB_oci8_forge extends CI_DB_forge { default: return; } } - } diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php index 19b7b744b..63df2963d 100644 --- a/system/database/drivers/odbc/odbc_driver.php +++ b/system/database/drivers/odbc/odbc_driver.php @@ -50,7 +50,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/database/ */ -class CI_DB_odbc_driver extends CI_DB { +class CI_DB_odbc_driver extends CI_DB_driver { /** * Database driver @@ -94,6 +94,22 @@ class CI_DB_odbc_driver extends CI_DB { // -------------------------------------------------------------------- /** + * ODBC result ID resource returned from odbc_prepare() + * + * @var resource + */ + private $odbc_result; + + /** + * Values to use with odbc_execute() for prepared statements + * + * @var array + */ + private $binds = array(); + + // -------------------------------------------------------------------- + + /** * Class constructor * * @param array $params @@ -128,6 +144,74 @@ class CI_DB_odbc_driver extends CI_DB { // -------------------------------------------------------------------- /** + * Compile Bindings + * + * @param string $sql SQL statement + * @param array $binds An array of values to bind + * @return string + */ + public function compile_binds($sql, $binds) + { + if (empty($binds) OR empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE) + { + return $sql; + } + elseif ( ! is_array($binds)) + { + $binds = array($binds); + $bind_count = 1; + } + else + { + // Make sure we're using numeric keys + $binds = array_values($binds); + $bind_count = count($binds); + } + + // We'll need the marker length later + $ml = strlen($this->bind_marker); + + // Make sure not to replace a chunk inside a string that happens to match the bind marker + if ($c = preg_match_all("/'[^']*'/i", $sql, $matches)) + { + $c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', + str_replace($matches[0], + str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]), + $sql, $c), + $matches, PREG_OFFSET_CAPTURE); + + // Bind values' count must match the count of markers in the query + if ($bind_count !== $c) + { + return $sql; + } + } + elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker, '/').'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count) + { + return $sql; + } + + if ($this->bind_marker !== '?') + { + do + { + $c--; + $sql = substr_replace($sql, '?', $matches[0][$c][1], $ml); + } + while ($c !== 0); + } + + if (FALSE !== ($this->odbc_result = odbc_prepare($this->conn_id, $sql))) + { + $this->binds = array_values($binds); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** * Execute the query * * @param string $sql an SQL query @@ -135,7 +219,25 @@ class CI_DB_odbc_driver extends CI_DB { */ protected function _execute($sql) { - return odbc_exec($this->conn_id, $sql); + if ( ! isset($this->odbc_result)) + { + return odbc_exec($this->conn_id, $sql); + } + elseif ($this->odbc_result === FALSE) + { + return FALSE; + } + + if (TRUE === ($success = odbc_execute($this->odbc_result, $this->binds))) + { + // For queries that return result sets, return the result_id resource on success + $this->is_write_type($sql) OR $success = $this->odbc_result; + } + + $this->odbc_result = NULL; + $this->binds = array(); + + return $success; } // -------------------------------------------------------------------- @@ -214,7 +316,7 @@ class CI_DB_odbc_driver extends CI_DB { */ protected function _escape_str($str) { - return remove_invisible_characters($str); + $this->db->display_error('db_unsupported_feature'); } // -------------------------------------------------------------------- @@ -312,58 +414,6 @@ class CI_DB_odbc_driver extends CI_DB { // -------------------------------------------------------------------- /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @param string $table - * @param array $values - * @return string - */ - protected function _update($table, $values) - { - $this->qb_limit = FALSE; - $this->qb_orderby = array(); - return parent::_update($table, $values); - } - - // -------------------------------------------------------------------- - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * - * If the database does not support the TRUNCATE statement, - * then this method maps to 'DELETE FROM table' - * - * @param string $table - * @return string - */ - protected function _truncate($table) - { - return 'DELETE FROM '.$table; - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @param string $table - * @return string - */ - protected function _delete($table) - { - $this->qb_limit = FALSE; - return parent::_delete($table); - } - - // -------------------------------------------------------------------- - - /** * Close DB Connection * * @return void @@ -372,5 +422,4 @@ class CI_DB_odbc_driver extends CI_DB { { odbc_close($this->conn_id); } - } diff --git a/system/database/drivers/pdo/pdo_driver.php b/system/database/drivers/pdo/pdo_driver.php index c6f84e0f9..c27607e55 100644 --- a/system/database/drivers/pdo/pdo_driver.php +++ b/system/database/drivers/pdo/pdo_driver.php @@ -126,7 +126,10 @@ class CI_DB_pdo_driver extends CI_DB { */ public function db_connect($persistent = FALSE) { - $this->options[PDO::ATTR_PERSISTENT] = $persistent; + if ($persistent === TRUE) + { + $this->options[PDO::ATTR_PERSISTENT] = TRUE; + } try { diff --git a/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php b/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php index 5f854061d..b5b95078e 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php +++ b/system/database/drivers/pdo/subdrivers/pdo_cubrid_forge.php @@ -178,6 +178,9 @@ class CI_DB_pdo_cubrid_forge extends CI_DB_pdo_forge { $attributes['TYPE'] = 'INTEGER'; $attributes['UNSIGNED'] = FALSE; return; + case 'LONGTEXT': + $attributes['TYPE'] = 'STRING'; + return; default: return; } } diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php index 84695ee9b..9a1cbcaf4 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_driver.php @@ -126,7 +126,12 @@ class CI_DB_pdo_dblib_driver extends CI_DB_pdo_driver { */ public function db_connect($persistent = FALSE) { - $this->conn_id = parent::db_connect($persistent); + if ($persistent === TRUE) + { + log_message('debug', "dblib driver doesn't support persistent connections"); + } + + $this->conn_id = parent::db_connect(FALSE); if ( ! is_object($this->conn_id)) { diff --git a/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php b/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php index d5dd9aad1..830200325 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php +++ b/system/database/drivers/pdo/subdrivers/pdo_dblib_forge.php @@ -111,6 +111,11 @@ class CI_DB_pdo_dblib_forge extends CI_DB_pdo_forge { */ protected function _attr_type(&$attributes) { + if (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE) + { + unset($attributes['CONSTRAINT']); + } + switch (strtoupper($attributes['TYPE'])) { case 'MEDIUMINT': diff --git a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php index 70f2bfd4e..38a5a8aff 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_mysql_driver.php @@ -218,6 +218,7 @@ class CI_DB_pdo_mysql_driver extends CI_DB_pdo_driver { if (FALSE !== $this->simple_query('USE '.$this->escape_identifiers($database))) { $this->database = $database; + $this->data_cache = array(); return TRUE; } diff --git a/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php b/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php index 94a52ffc8..705b1c711 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php +++ b/system/database/drivers/pdo/subdrivers/pdo_oci_forge.php @@ -173,5 +173,4 @@ class CI_DB_pdo_oci_forge extends CI_DB_pdo_forge { default: return; } } - } diff --git a/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php index 333448838..82554ec80 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php +++ b/system/database/drivers/pdo/subdrivers/pdo_odbc_driver.php @@ -161,6 +161,19 @@ class CI_DB_pdo_odbc_driver extends CI_DB_pdo_driver { // -------------------------------------------------------------------- /** + * Platform-dependant string escape + * + * @param string + * @return string + */ + protected function _escape_str($str) + { + $this->db->display_error('db_unsupported_feature'); + } + + // -------------------------------------------------------------------- + + /** * Determines if a query is a "write" type. * * @param string An SQL query string @@ -213,72 +226,4 @@ class CI_DB_pdo_odbc_driver extends CI_DB_pdo_driver { { return 'SELECT column_name FROM information_schema.columns WHERE table_name = '.$this->escape($table); } - - // -------------------------------------------------------------------- - - /** - * Update statement - * - * Generates a platform-specific update string from the supplied data - * - * @param string $table - * @param array $values - * @return string - */ - protected function _update($table, $values) - { - $this->qb_limit = FALSE; - $this->qb_orderby = array(); - return parent::_update($table, $values); - } - - // -------------------------------------------------------------------- - - /** - * Truncate statement - * - * Generates a platform-specific truncate string from the supplied data - * - * If the database does not support the TRUNCATE statement, - * then this method maps to 'DELETE FROM table' - * - * @param string $table - * @return string - */ - protected function _truncate($table) - { - return 'DELETE FROM '.$table; - } - - // -------------------------------------------------------------------- - - /** - * Delete statement - * - * Generates a platform-specific delete string from the supplied data - * - * @param string the table name - * @return string - */ - protected function _delete($table) - { - $this->qb_limit = FALSE; - return parent::_delete($table); - } - - // -------------------------------------------------------------------- - - /** - * LIMIT - * - * Generates a platform-specific LIMIT clause - * - * @param string $sql SQL Query - * @return string - */ - protected function _limit($sql) - { - return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$this->qb_limit.' ', $sql); - } - } diff --git a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php index 602f895f1..56bf87f3a 100644 --- a/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php +++ b/system/database/drivers/pdo/subdrivers/pdo_sqlsrv_forge.php @@ -111,6 +111,11 @@ class CI_DB_pdo_sqlsrv_forge extends CI_DB_pdo_forge { */ protected function _attr_type(&$attributes) { + if (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE) + { + unset($attributes['CONSTRAINT']); + } + switch (strtoupper($attributes['TYPE'])) { case 'MEDIUMINT': diff --git a/system/database/drivers/sqlsrv/sqlsrv_driver.php b/system/database/drivers/sqlsrv/sqlsrv_driver.php index 0cd9ce16d..c55d5f7b7 100644 --- a/system/database/drivers/sqlsrv/sqlsrv_driver.php +++ b/system/database/drivers/sqlsrv/sqlsrv_driver.php @@ -171,6 +171,7 @@ class CI_DB_sqlsrv_driver extends CI_DB { if ($this->_execute('USE '.$this->escape_identifiers($database))) { $this->database = $database; + $this->data_cache = array(); return TRUE; } diff --git a/system/database/drivers/sqlsrv/sqlsrv_forge.php b/system/database/drivers/sqlsrv/sqlsrv_forge.php index f915dad3e..4f0ce9d6f 100644 --- a/system/database/drivers/sqlsrv/sqlsrv_forge.php +++ b/system/database/drivers/sqlsrv/sqlsrv_forge.php @@ -111,6 +111,11 @@ class CI_DB_sqlsrv_forge extends CI_DB_forge { */ protected function _attr_type(&$attributes) { + if (isset($attributes['CONSTRAINT']) && strpos($attributes['TYPE'], 'INT') !== FALSE) + { + unset($attributes['CONSTRAINT']); + } + switch (strtoupper($attributes['TYPE'])) { case 'MEDIUMINT': diff --git a/system/helpers/file_helper.php b/system/helpers/file_helper.php index 0d8d1d0d9..3cb36a551 100644 --- a/system/helpers/file_helper.php +++ b/system/helpers/file_helper.php @@ -138,13 +138,15 @@ if ( ! function_exists('delete_files')) { if ($filename !== '.' && $filename !== '..') { - if (is_dir($path.DIRECTORY_SEPARATOR.$filename) && $filename[0] !== '.') + $filepath = $path.DIRECTORY_SEPARATOR.$filename; + + if (is_dir($filepath) && $filename[0] !== '.' && ! is_link($filepath)) { - delete_files($path.DIRECTORY_SEPARATOR.$filename, $del_dir, $htdocs, $_level + 1); + delete_files($filepath, $del_dir, $htdocs, $_level + 1); } elseif ($htdocs !== TRUE OR ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename)) { - @unlink($path.DIRECTORY_SEPARATOR.$filename); + @unlink($filepath); } } } diff --git a/system/helpers/path_helper.php b/system/helpers/path_helper.php index 18e175093..6c846a211 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|php:\/\/)#i', $path) OR filter_var($path, FILTER_VALIDATE_IP) === $path ) + if (preg_match('#^(http:\/\/|https:\/\/|www\.|ftp|php:\/\/)#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/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php index ca3997ad5..ab8bfab8b 100644 --- a/system/libraries/Cache/drivers/Cache_memcached.php +++ b/system/libraries/Cache/drivers/Cache_memcached.php @@ -307,7 +307,7 @@ class CI_Cache_memcached extends CI_Driver { { $this->_memcached->close(); } - elseif ($this->_memcached instanceof Memcached) + elseif ($this->_memcached instanceof Memcached && method_exists($this->_memcached, 'quit')) { $this->_memcached->quit(); } diff --git a/system/libraries/Email.php b/system/libraries/Email.php index 0a55e1841..e9f69f065 100644 --- a/system/libraries/Email.php +++ b/system/libraries/Email.php @@ -387,18 +387,8 @@ class CI_Email { public function __construct(array $config = array()) { $this->charset = config_item('charset'); - - if (count($config) > 0) - { - $this->initialize($config); - } - else - { - $this->_smtp_auth = ! ($this->smtp_user === '' && $this->smtp_pass === ''); - } - + $this->initialize($config); $this->_safe_mode = ( ! is_php('5.4') && ini_get('safe_mode')); - $this->charset = strtoupper($this->charset); log_message('info', 'Email Class Initialized'); } @@ -406,28 +396,15 @@ class CI_Email { // -------------------------------------------------------------------- /** - * Destructor - Releases Resources - * - * @return void - */ - public function __destruct() - { - if (is_resource($this->_smtp_connect)) - { - $this->_send_command('quit'); - } - } - - // -------------------------------------------------------------------- - - /** * Initialize preferences * - * @param array + * @param array $config * @return CI_Email */ - public function initialize($config = array()) + public function initialize(array $config = array()) { + $this->clear(); + foreach ($config as $key => $val) { if (isset($this->$key)) @@ -444,9 +421,9 @@ class CI_Email { } } } - $this->clear(); - $this->_smtp_auth = ! ($this->smtp_user === '' && $this->smtp_pass === ''); + $this->charset = strtoupper($this->charset); + $this->_smtp_auth = isset($this->smtp_user[0], $this->smtp_pass[0]); return $this; } @@ -1942,6 +1919,7 @@ class CI_Email { if ( ! $this->_send_command('from', $this->clean_email($this->_headers['From']))) { + $this->_smtp_end(); return FALSE; } @@ -1949,6 +1927,7 @@ class CI_Email { { if ( ! $this->_send_command('to', $val)) { + $this->_smtp_end(); return FALSE; } } @@ -1959,6 +1938,7 @@ class CI_Email { { if ($val !== '' && ! $this->_send_command('to', $val)) { + $this->_smtp_end(); return FALSE; } } @@ -1970,6 +1950,7 @@ class CI_Email { { if ($val !== '' && ! $this->_send_command('to', $val)) { + $this->_smtp_end(); return FALSE; } } @@ -1977,6 +1958,7 @@ class CI_Email { if ( ! $this->_send_command('data')) { + $this->_smtp_end(); return FALSE; } @@ -1986,30 +1968,38 @@ class CI_Email { $this->_send_data('.'); $reply = $this->_get_smtp_data(); - $this->_set_error_message($reply); + $this->_smtp_end(); + if (strpos($reply, '250') !== 0) { $this->_set_error_message('lang:email_smtp_error', $reply); return FALSE; } - if ($this->smtp_keepalive) - { - $this->_send_command('reset'); - } - else - { - $this->_send_command('quit'); - } - return TRUE; } // -------------------------------------------------------------------- /** + * SMTP End + * + * Shortcut to send RSET or QUIT depending on keep-alive + * + * @return void + */ + protected function _smtp_end() + { + ($this->smtp_keepalive) + ? $this->_send_command('reset') + : $this->_send_command('quit'); + } + + // -------------------------------------------------------------------- + + /** * SMTP Connect * * @return string @@ -2193,6 +2183,11 @@ class CI_Email { return FALSE; } + if ($this->smtp_keepalive) + { + $this->_smtp_auth = FALSE; + } + return TRUE; } @@ -2382,4 +2377,15 @@ class CI_Email { return 'application/x-unknown-content-type'; } + // -------------------------------------------------------------------- + + /** + * Destructor + * + * @return void + */ + public function __destruct() + { + is_resource($this->_smtp_connect) && $this->_send_command('quit'); + } } diff --git a/system/libraries/Encryption.php b/system/libraries/Encryption.php index 92c38a0ed..a10a5c20c 100644 --- a/system/libraries/Encryption.php +++ b/system/libraries/Encryption.php @@ -339,12 +339,26 @@ class CI_Encryption { { if (function_exists('random_bytes')) { - return random_bytes((int) $length); + try + { + return random_bytes((int) $length); + } + catch (Exception $e) + { + log_message('error', $e->getMessage()); + return FALSE; + } + } + elseif (defined('MCRYPT_DEV_URANDOM')) + { + return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); } - return ($this->_driver === 'mcrypt') - ? mcrypt_create_iv($length, MCRYPT_DEV_URANDOM) - : openssl_random_pseudo_bytes($length); + $is_secure = NULL; + $key = openssl_random_pseudo_bytes($length, $is_secure); + return ($is_secure === TRUE) + ? $key + : FALSE; } // -------------------------------------------------------------------- @@ -400,7 +414,7 @@ class CI_Encryption { // The greater-than-1 comparison is mostly a work-around for a bug, // where 1 is returned for ARCFour instead of 0. $iv = (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1) - ? mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM) + ? $this->create_key($iv_size) : NULL; if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0) @@ -463,7 +477,7 @@ class CI_Encryption { } $iv = ($iv_size = openssl_cipher_iv_length($params['handle'])) - ? openssl_random_pseudo_bytes($iv_size) + ? $this->create_key($iv_size) : NULL; $data = openssl_encrypt( diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php index e4a518957..04445f5b7 100644 --- a/system/libraries/Form_validation.php +++ b/system/libraries/Form_validation.php @@ -494,6 +494,63 @@ class CI_Form_validation { // -------------------------------------------------------------------- /** + * Prepare rules + * + * Re-orders the provided rules in order of importance, so that + * they can easily be executed later without weird checks ... + * + * "Callbacks" are given the highest priority (always called), + * followed by 'required' (called if callbacks didn't fail), + * and then every next rule depends on the previous one passing. + * + * @param array $rules + * @return array + */ + protected function _prepare_rules($rules) + { + $new_rules = array(); + $callbacks = array(); + + foreach ($rules as &$rule) + { + // Let 'required' always be the first (non-callback) rule + if ($rule === 'required') + { + array_unshift($new_rules, 'required'); + } + // 'isset' is a kind of a weird alias for 'required' ... + elseif ($rule === 'isset' && (empty($new_rules) OR $new_rules[0] !== 'required')) + { + array_unshift($new_rules, 'isset'); + } + // The old/classic 'callback_'-prefixed rules + elseif (is_string($rule) && strncmp('callback_', $rule, 9) === 0) + { + $callbacks[] = $rule; + } + // Proper callables + elseif (is_callable($rule)) + { + $callbacks[] = $rule; + } + // "Named" callables; i.e. array('name' => $callable) + elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1])) + { + $callbacks[] = $rule; + } + // Everything else goes at the end of the queue + else + { + $new_rules[] = $rule; + } + } + + return array_merge($callbacks, $new_rules); + } + + // -------------------------------------------------------------------- + + /** * Traverse a multidimensional $_POST array index until the data is found * * @param array @@ -580,70 +637,7 @@ class CI_Form_validation { return; } - // If the field is blank, but NOT required, no further tests are necessary - $callback = FALSE; - if ( ! in_array('required', $rules) && ($postdata === NULL OR $postdata === '')) - { - // Before we bail out, does the rule contain a callback? - foreach ($rules as &$rule) - { - if (is_string($rule)) - { - if (strncmp($rule, 'callback_', 9) === 0) - { - $callback = TRUE; - $rules = array(1 => $rule); - break; - } - } - elseif (is_callable($rule)) - { - $callback = TRUE; - $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) - { - return; - } - } - - // Isset Test. Typically this rule will only apply to checkboxes. - if (($postdata === NULL OR $postdata === '') && ! $callback) - { - if (in_array('isset', $rules, TRUE) OR in_array('required', $rules)) - { - // Set the message type - $type = in_array('required', $rules) ? 'required' : 'isset'; - - $line = $this->_get_error_message($type, $row['field']); - - // Build the error message - $message = $this->_build_error_msg($line, $this->_translate_fieldname($row['label'])); - - // Save the error message - $this->_field_data[$row['field']]['error'] = $message; - - if ( ! isset($this->_error_array[$row['field']])) - { - $this->_error_array[$row['field']] = $message; - } - } - - return; - } - - // -------------------------------------------------------------------- - - // Cycle through each rule and run it + $rules = $this->_prepare_rules($rules); foreach ($rules as $rule) { $_in_array = FALSE; @@ -702,6 +696,17 @@ class CI_Form_validation { $param = $match[2]; } + // Ignore empty, non-required inputs with a few exceptions ... + if ( + ($postdata === NULL OR $postdata === '') + && $callback === FALSE + && $callable === FALSE + && ! in_array($rule, array('required', 'isset', 'matches'), TRUE) + ) + { + continue; + } + // Call the function that corresponds to the rule if ($callback OR $callable !== FALSE) { @@ -740,12 +745,6 @@ class CI_Form_validation { { $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result; } - - // If the field isn't required and we just processed a callback we'll move on... - if ( ! in_array('required', $rules, TRUE) && $result !== FALSE) - { - continue; - } } elseif ( ! method_exists($this, $rule)) { @@ -1055,7 +1054,9 @@ class CI_Form_validation { */ public function required($str) { - return is_array($str) ? (bool) count($str) : (trim($str) !== ''); + return is_array($str) + ? (empty($str) === FALSE) + : (trim($str) !== ''); } // -------------------------------------------------------------------- diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php index f594b7125..24fe8c68d 100644 --- a/system/libraries/Image_lib.php +++ b/system/libraries/Image_lib.php @@ -456,7 +456,7 @@ class CI_Image_lib { { if (property_exists($this, $key)) { - if (in_array($key, array('wm_font_color', 'wm_shadow_color'))) + if (in_array($key, array('wm_font_color', 'wm_shadow_color'), TRUE)) { if (preg_match('/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i', $val, $matches)) { @@ -478,6 +478,10 @@ class CI_Image_lib { continue; } } + elseif (in_array($key, array('width', 'height'), TRUE) && ! ctype_digit((string) $val)) + { + continue; + } $this->$key = $val; } @@ -862,27 +866,28 @@ class CI_Image_lib { if ($action === 'crop') { - $cmd .= ' -crop '.$this->width.'x'.$this->height.'+'.$this->x_axis.'+'.$this->y_axis.' "'.$this->full_src_path.'" "'.$this->full_dst_path .'" 2>&1'; + $cmd .= ' -crop '.$this->width.'x'.$this->height.'+'.$this->x_axis.'+'.$this->y_axis; } elseif ($action === 'rotate') { - $angle = ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt') - ? '-flop' : '-rotate '.$this->rotation_angle; - - $cmd .= ' '.$angle.' "'.$this->full_src_path.'" "'.$this->full_dst_path.'" 2>&1'; + $cmd .= ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt') + ? ' -flop' + : ' -rotate '.$this->rotation_angle; } else // Resize { if($this->maintain_ratio === TRUE) { - $cmd .= ' -resize '.$this->width.'x'.$this->height.' "'.$this->full_src_path.'" "'.$this->full_dst_path.'" 2>&1'; + $cmd .= ' -resize '.$this->width.'x'.$this->height; } else { - $cmd .= ' -resize '.$this->width.'x'.$this->height.'\! "'.$this->full_src_path.'" "'.$this->full_dst_path.'" 2>&1'; + $cmd .= ' -resize '.$this->width.'x'.$this->height.'\!'; } } + $cmd .= ' "'.escapeshellarg($this->full_src_path).'" "'.escapeshellarg($this->full_dst_path).'" 2>&1'; + $retval = 1; // exec() might be disabled if (function_usable('exec')) diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php index dde84a775..3b391a8ef 100644 --- a/system/libraries/Session/Session.php +++ b/system/libraries/Session/Session.php @@ -91,6 +91,7 @@ class CI_Session { // Note: BC workaround elseif (config_item('sess_use_database')) { + log_message('debug', 'Session: "sess_driver" is empty; using BC fallback to "sess_use_database".'); $this->_driver = 'database'; } diff --git a/system/libraries/Session/drivers/Session_database_driver.php b/system/libraries/Session/drivers/Session_database_driver.php index 317bd7d4d..cb152f91f 100644 --- a/system/libraries/Session/drivers/Session_database_driver.php +++ b/system/libraries/Session/drivers/Session_database_driver.php @@ -109,7 +109,10 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan } // Note: BC work-around for the old 'sess_table_name' setting, should be removed in the future. - isset($this->_config['save_path']) OR $this->_config['save_path'] = config_item('sess_table_name'); + if ( ! isset($this->_config['save_path']) && ($this->_config['save_path'] = config_item('sess_table_name'))) + { + log_message('debug', 'Session: "sess_save_path" is empty; using BC fallback to "sess_table_name".'); + } } // ------------------------------------------------------------------------ diff --git a/system/libraries/Session/drivers/Session_files_driver.php b/system/libraries/Session/drivers/Session_files_driver.php index 119bf6572..57c3777a2 100644 --- a/system/libraries/Session/drivers/Session_files_driver.php +++ b/system/libraries/Session/drivers/Session_files_driver.php @@ -95,6 +95,7 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle } else { + log_message('debug', 'Session: "sess_save_path" is empty; using "session.save_path" value from php.ini.'); $this->_config['save_path'] = rtrim(ini_get('session.save_path'), '/\\'); } } diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php index d3a265958..592f1ff6c 100644 --- a/system/libraries/Session/drivers/Session_redis_driver.php +++ b/system/libraries/Session/drivers/Session_redis_driver.php @@ -279,7 +279,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle if ($this->_redis->ping() === '+PONG') { $this->_release_lock(); - if ($this->_redis->close() === $this->_failure) + if ($this->_redis->close() === FALSE) { return $this->_fail(); } diff --git a/system/libraries/User_agent.php b/system/libraries/User_agent.php index c4e11592d..60d159966 100644 --- a/system/libraries/User_agent.php +++ b/system/libraries/User_agent.php @@ -173,13 +173,11 @@ class CI_User_agent { */ public function __construct() { + $this->_load_agent_file(); + if (isset($_SERVER['HTTP_USER_AGENT'])) { $this->agent = trim($_SERVER['HTTP_USER_AGENT']); - } - - if ($this->agent !== NULL && $this->_load_agent_file()) - { $this->_compile_data(); } diff --git a/tests/codeigniter/core/Config_test.php b/tests/codeigniter/core/Config_test.php index 26a5f32f5..b5c9e849d 100644 --- a/tests/codeigniter/core/Config_test.php +++ b/tests/codeigniter/core/Config_test.php @@ -127,6 +127,8 @@ class Config_test extends CI_TestCase { $this->assertEquals($index_page.'/'.$uri, $this->config->site_url($uri)); $this->assertEquals($index_page.'/'.$uri.'/'.$uri2, $this->config->site_url(array($uri, $uri2))); + $this->assertEquals($index_page.'/test/', $this->config->site_url('test/')); + $suffix = 'ing'; $this->config->set_item('url_suffix', $suffix); diff --git a/tests/codeigniter/core/Input_test.php b/tests/codeigniter/core/Input_test.php index c56900d22..e1f4011b5 100644 --- a/tests/codeigniter/core/Input_test.php +++ b/tests/codeigniter/core/Input_test.php @@ -262,6 +262,12 @@ class Input_test extends CI_TestCase { $this->assertEquals('FE80:0000:0000:0000:0202:B3FF:FE1E:8300', $this->input->ip_address()); $this->input->ip_address = FALSE; + $this->ci_set_config('proxy_ips', '0::/32'); + $_SERVER['HTTP_CLIENT_IP'] = '127.0.0.7'; + $_SERVER['REMOTE_ADDR'] = '0000:0000:0000:0000:0000:0000:0000:0001'; + $this->assertEquals('127.0.0.7', $this->input->ip_address()); + + $this->input->ip_address = FALSE; $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; // back to reality } diff --git a/tests/codeigniter/libraries/Form_validation_test.php b/tests/codeigniter/libraries/Form_validation_test.php index 3537eb355..b87ca65ff 100644 --- a/tests/codeigniter/libraries/Form_validation_test.php +++ b/tests/codeigniter/libraries/Form_validation_test.php @@ -40,11 +40,22 @@ class Form_validation_test extends CI_TestCase { public function test_rule_required() { - $rules = array(array('field' => 'foo', 'label' => 'foo_label', 'rules' => 'required')); - $this->assertTrue($this->run_rules($rules, array('foo' => 'bar'))); + $rules = array(array('field' => 'foo', 'label' => 'Foo', 'rules' => 'is_numeric')); + // Empty, not required + $this->assertTrue($this->run_rules($rules, array('foo' => ''))); + + + // Not required, but also not empty + $this->assertTrue($this->run_rules($rules, array('foo' => '123'))); + $this->assertFalse($this->run_rules($rules, array('foo' => 'bar'))); + + // Required variations + $rules[0]['rules'] .= '|required'; + $this->assertTrue($this->run_rules($rules, array('foo' => '123'))); $this->assertFalse($this->run_rules($rules, array('foo' => ''))); $this->assertFalse($this->run_rules($rules, array('foo' => ' '))); + $this->assertFalse($this->run_rules($rules, array('foo' => 'bar'))); } public function test_rule_matches() @@ -55,9 +66,9 @@ class Form_validation_test extends CI_TestCase { ); $values_base = array('foo' => 'sample'); - $this->assertTrue($this->run_rules($rules, array_merge($values_base, array('bar' => '')))); $this->assertTrue($this->run_rules($rules, array_merge($values_base, array('bar' => 'sample')))); + $this->assertFalse($this->run_rules($rules, array_merge($values_base, array('bar' => '')))); $this->assertFalse($this->run_rules($rules, array_merge($values_base, array('bar' => 'Sample')))); $this->assertFalse($this->run_rules($rules, array_merge($values_base, array('bar' => ' sample')))); } diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst index 20c7c795d..f9e7f572c 100644 --- a/user_guide_src/source/changelog.rst +++ b/user_guide_src/source/changelog.rst @@ -2,7 +2,7 @@ Change Log ########## -Version 3.1.0 +Version 3.2.0 ============= Release Date: Not Released @@ -38,12 +38,70 @@ Bug fixes for 3.1.0 - Fixed a bug (#4528) - :doc:`Cache Library <libraries/caching>` stored all scalar values as strings with the 'redis' driver. -Version 3.0.7 +Version 3.1.1 ============= Release Date: Not Released + +Bug fixes for 3.1.1 +------------------- + + + + +Version 3.1.0 +============= + +Release Date: July 26, 2016 + +- **Security** + + - Fixed an SQL injection in the 'odbc' database driver. + - Updated :php:func:`set_realpath()` :doc:`Path Helpr <helpers/path_helper>` function to filter-out ``php://`` wrapper inputs. + - Officially dropped any kind of support for PHP 5.2.x and anything under 5.3.7. + +- General Changes + + - Updated :doc:`Image Manipulation Library <libraries/image_lib>` to validate *width* and *height* configuration values. + - Updated :doc:`Encryption Library <libraries/encryption>` to always prefer ``random_bytes()`` when it is available. + - Updated :doc:`Session Library <libraries/sessions>` to log 'debug' messages when using fallbacks to *session.save_path* (php.ini) or 'sess_use_database', 'sess_table_name' settings. + - Added a 'LONGTEXT' to 'STRING' alias to :doc:`Database Forge <database/forge>` for the 'cubrid', 'pdo/cubrid' drivers. + - Added 'TINYINT', 'MEDIUMINT', 'INT' and 'BIGINT' aliases to 'NUMBER' to :doc:`Database Forge <database/forge>` for the 'oci8', 'pdo/oci' drivers. + + - :php:func:`password_hash()` :doc:`compatibility function <general/compatibility_functions>` changes: + + - Changed salt-generation logic to prefer ``random_bytes()`` when it is available. + - Changed salt-generation logic to prefer direct access to */dev/urandom* over ``openssl_random_pseudo_bytes()``. + - Changed salt-generation logic to error if ``openssl_random_pseudo_bytes()`` sets its ``$crypto_strong`` flag to FALSE. + +Bug fixes for 3.1.0 +------------------- + +- Fixed a bug where :doc:`Image Manipulation Library <libraries/image_lib>` didn't escape image source paths passed to ImageMagick as shell arguments. +- Fixed a bug (#861) - :doc:`Database Forge <database/forge>` method ``create_table()`` incorrectly accepts field width constraints for MSSQL/SQLSRV integer-type columns. +- Fixed a bug (#4562) - :doc:`Cache Library <libraries/caching>` didn't check if ``Memcached::quit()`` is available before calling it. +- Fixed a bug (#4563) - :doc:`Input Library <libraries/input>` method ``request_headers()`` ignores ``$xss_clean`` parameter value after first call. +- Fixed a bug (#4605) - :doc:`Config Library <libraries/config>` method ``site_url()`` stripped trailing slashes from relative URIs passed to it. +- Fixed a bug (#4613) - :doc:`Email Library <libraries/config>` failed to send multiple emails via SMTP due to "already authenticated" errors when keep-alive is enabled. +- Fixed a bug (#4633) - :doc:`Form Validation Library <libraries/form_validation>` ignored multiple "callback" rules for empty, non-required fields. +- Fixed a bug (#4637) - :doc:`Database <database/index>` method ``error()`` returned ``FALSE`` with the 'oci8' driver if there was no error. +- Fixed a bug (#4647) - :doc:`Query Builder <database/query_builder>` method ``count_all_results()`` doesn't take into account ``GROUP BY`` clauses while deciding whether to do a subquery or not. +- Fixed a bug where :doc:`Session Library <libraries/sessions>` 'redis' driver didn't properly detect if a connection is properly closed on PHP 5.x. +- Fixed a bug (#4583) - :doc:`Email Library <libraries/email>` didn't properly handle inline attachments in HTML emails. +- Fixed a bug where :doc:`Database <database/index>` method ``db_select()`` didn't clear metadata cached for the previously used database. +- Fixed a bug (#4675) - :doc:`File Helper <helpers/file_helper>` function :php:func:`delete_files()` treated symbolic links as regular directories. +- Fixed a bug (#4674) - :doc:`Database <database/index>` driver 'dblib' triggered E_WARNING messages while connecting. +- Fixed a bug (#4678) - :doc:`Database Forge <database/forge>` tried to use unsupported ``IF NOT EXISTS`` clause when creating tables on Oracle. +- Fixed a bug (#4691) - :doc:`File Uploading Library <libraries/file_uploading>` method ``data()`` returns wrong 'raw_name' when the filename extension is also contained in the raw filename. +- Fixed a bug (#4679) - :doc:`Input Library <libraries/input>` method ``ip_address()`` errors with a matching ``$config['proxy_ips']`` IPv6 address. +- Fixed a bug (#4695) - :doc:`User Agent Library <libraries/user_agent>` didn't load the *config/user_agents.php* file when there's no ``User-Agent`` HTTP request header. +- Fixed a bug (#4713) - :doc:`Query Builder <database/query_builder>` methods ``insert_batch()``, ``update_batch()`` could return wrong affected rows count. +- Fixed a bug (#4712) - :doc:`Email Library <libraries/email>` doesn't sent ``RSET`` to SMTP servers after a failure and while using keep-alive. +- Fixed a bug (#4724) - :doc:`Common function <general/common_functions>` :php:func:`is_https()` compared the ``X-Forwarded-Proto`` HTTP header case-sensitively. +- Fixed a bug (#4725) - :doc:`Common function <general/common_functions>` :php:func:`remove_invisible_characters()` searched case-sensitively for URL-encoded characters. + Version 3.0.6 ============= diff --git a/user_guide_src/source/conf.py b/user_guide_src/source/conf.py index cd060ed67..4714b0d62 100644 --- a/user_guide_src/source/conf.py +++ b/user_guide_src/source/conf.py @@ -48,9 +48,9 @@ copyright = u'2014 - 2016, British Columbia Institute of Technology' # built documents. # # The short X.Y version. -version = '3.1.0-dev' +version = '3.2.0-dev' # The full version, including alpha/beta/rc tags. -release = '3.1.0-dev' +release = '3.2.0-dev' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/user_guide_src/source/contributing/index.rst b/user_guide_src/source/contributing/index.rst index 64e9f0d92..be776ec1f 100644 --- a/user_guide_src/source/contributing/index.rst +++ b/user_guide_src/source/contributing/index.rst @@ -103,10 +103,10 @@ must also be updated for every change. Also PHPDoc blocks must be maintained. Compatibility ============= -CodeIgniter recommends PHP 5.5 or newer to be used, but it should be -compatible with PHP 5.2.4 so all code supplied must stick to this -requirement. If PHP 5.3 (and above) functions or features are used then -there must be a fallback for PHP 5.2.4. +CodeIgniter recommends PHP 5.6 or newer to be used, but it should be +compatible with PHP 5.3.7 so all code supplied must stick to this +requirement. If PHP 5.4 (and above) functions or features are used then +there must be a fallback for PHP 5.3.7. Branching ========= diff --git a/user_guide_src/source/database/db_driver_reference.rst b/user_guide_src/source/database/db_driver_reference.rst index db0c67118..6f2fa5fb1 100644 --- a/user_guide_src/source/database/db_driver_reference.rst +++ b/user_guide_src/source/database/db_driver_reference.rst @@ -108,6 +108,16 @@ This article is intended to be a reference for them. for use when you don't need to get a result object or to just send a query to the database and not care for the result. + .. php:method:: affected_rows() + + :returns: Number of rows affected + :rtype: int + + Returns the number of rows *changed* by the last executed query. + + Useful for checking how much rows were created, updated or deleted + during the last executed query. + .. php:method:: trans_strict([$mode = TRUE]) :param bool $mode: Strict mode flag diff --git a/user_guide_src/source/general/errors.rst b/user_guide_src/source/general/errors.rst index 9c190feb1..a1cc3517a 100644 --- a/user_guide_src/source/general/errors.rst +++ b/user_guide_src/source/general/errors.rst @@ -78,11 +78,10 @@ The following functions let you generate errors: CodeIgniter automatically logs any ``show_404()`` calls. Setting the optional second parameter to FALSE will skip logging. -.. php:function:: log_message($level, $message, $php_error = FALSE) +.. php:function:: log_message($level, $message) :param string $level: Log level: 'error', 'debug' or 'info' :param string $message: Message to log - :param bool $php_error: Whether we're logging a native PHP error message :rtype: void This function lets you write messages to your log files. You must diff --git a/user_guide_src/source/general/requirements.rst b/user_guide_src/source/general/requirements.rst index 7eea71745..f2729f3d5 100644 --- a/user_guide_src/source/general/requirements.rst +++ b/user_guide_src/source/general/requirements.rst @@ -2,9 +2,9 @@ Server Requirements ################### -`PHP <http://php.net/>`_ version 5.5 or newer is recommended. +`PHP <http://php.net/>`_ version 5.6 or newer is recommended. -It should work on 5.2.4 as well, but we strongly advise you NOT to run +It should work on 5.3.7 as well, but we strongly advise you NOT to run such old versions of PHP, because of potential security and performance issues, as well as missing features. diff --git a/user_guide_src/source/general/styleguide.rst b/user_guide_src/source/general/styleguide.rst index b21246b4f..9b4a84e14 100644 --- a/user_guide_src/source/general/styleguide.rst +++ b/user_guide_src/source/general/styleguide.rst @@ -345,8 +345,8 @@ inability for CodeIgniter to send proper headers. Compatibility ============= -CodeIgniter recommends PHP 5.5 or newer to be used, but it should be -compatible with PHP 5.2.4. Your code must either be compatible with this +CodeIgniter recommends PHP 5.6 or newer to be used, but it should be +compatible with PHP 5.3.7. Your code must either be compatible with this requirement, provide a suitable fallback, or be an optional feature that dies quietly without affecting a user's application. diff --git a/user_guide_src/source/helpers/inflector_helper.rst b/user_guide_src/source/helpers/inflector_helper.rst index 17dab57bf..df0c568c0 100644 --- a/user_guide_src/source/helpers/inflector_helper.rst +++ b/user_guide_src/source/helpers/inflector_helper.rst @@ -3,7 +3,7 @@ Inflector Helper ################ The Inflector Helper file contains functions that permits you to change -words to plural, singular, camel case, etc. +**English** words to plural, singular, camel case, etc. .. contents:: :local: diff --git a/user_guide_src/source/helpers/language_helper.rst b/user_guide_src/source/helpers/language_helper.rst index cadf3c0ce..cfbd6c057 100644 --- a/user_guide_src/source/helpers/language_helper.rst +++ b/user_guide_src/source/helpers/language_helper.rst @@ -27,17 +27,20 @@ The following functions are available: .. php:function:: lang($line[, $for = ''[, $attributes = array()]]) - :param string $line: Language line key - :param string $for: HTML "for" attribute (ID of the element we're creating a label for) - :param array $attributes: Any additional HTML attributes - :returns: HTML-formatted language line label + :param string $line: Language line key + :param string $for: HTML "for" attribute (ID of the element we're creating a label for) + :param array $attributes: Any additional HTML attributes + :returns: The language line; in an HTML label tag, if the ``$for`` parameter is not empty :rtype: string This function returns a line of text from a loaded language file with simplified syntax that may be more desirable for view files than ``CI_Lang::line()``. - Example:: + Examples:: + + echo lang('language_key'); + // Outputs: Language line echo lang('language_key', 'form_item_id', array('class' => 'myClass')); // Outputs: <label for="form_item_id" class="myClass">Language line</label>
\ No newline at end of file diff --git a/user_guide_src/source/index.rst b/user_guide_src/source/index.rst index a13ec983e..615c27f3c 100644 --- a/user_guide_src/source/index.rst +++ b/user_guide_src/source/index.rst @@ -116,7 +116,7 @@ Helper Reference installation/index general/index libraries/index - helpers/index database/index + helpers/index tutorial/index general/credits diff --git a/user_guide_src/source/installation/downloads.rst b/user_guide_src/source/installation/downloads.rst index 29f1a6d87..1e28a5bf0 100644 --- a/user_guide_src/source/installation/downloads.rst +++ b/user_guide_src/source/installation/downloads.rst @@ -2,8 +2,9 @@ Downloading CodeIgniter ####################### -- `CodeIgniter v3.1.0-dev (Current version) <https://codeload.github.com/bcit-ci/CodeIgniter/zip/develop>`_ -- `CodeIgniter v3.0.7-dev <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0-stable>`_ +- `CodeIgniter v3.2.0-dev (Current version) <https://codeload.github.com/bcit-ci/CodeIgniter/zip/develop>`_ +- `CodeIgniter v3.1.1-dev <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1-stable>`_ +- `CodeIgniter v3.1.0 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.1.0>`_ - `CodeIgniter v3.0.6 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.6>`_ - `CodeIgniter v3.0.5 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.5>`_ - `CodeIgniter v3.0.4 <https://codeload.github.com/bcit-ci/CodeIgniter/zip/3.0.4>`_ diff --git a/user_guide_src/source/installation/upgrade_210.rst b/user_guide_src/source/installation/upgrade_210.rst index 5874bfc86..866dcf4ae 100644 --- a/user_guide_src/source/installation/upgrade_210.rst +++ b/user_guide_src/source/installation/upgrade_210.rst @@ -13,11 +13,11 @@ Replace all files and directories in your "system" folder. .. note:: If you have any custom developed files in these folders please make copies of them first. -Step 2: Replace config/user_agents.php -====================================== +Step 2: Replace config/mimes.php +================================ This config file has been updated to contain more user agent types, -please copy it to _application/config/user_agents.php*. +please copy it to _application/config/mimes.php*. Step 3: Update your user guide ============================== diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst index 9a40f2b60..0fc211f89 100644 --- a/user_guide_src/source/installation/upgrade_300.rst +++ b/user_guide_src/source/installation/upgrade_300.rst @@ -842,7 +842,6 @@ CodeIgniter 3.1+. .. note:: This method is still available, but you're strongly encouraged to remove its usage sooner rather than later. -====================== The Javascript library ====================== @@ -854,6 +853,25 @@ It is now deprecated and scheduled for removal in CodeIgniter 3.1+. .. note:: This library is still available, but you're strongly encouraged to remove its usage sooner rather than later. +Form Validation method prep_for_form() +====================================== + +The :doc:`Form Validation Library <../libraries/form_validation>` has a +``prep_for_form()`` method, which is/can also be used as a rule in +``set_rules()`` to automatically perform HTML encoding on input data. + +Automatically encoding input (instead of output) data is a bad practice in +the first place, and CodeIgniter and PHP itself offer other alternatives +to this method anyway. +For example, :doc:`Form Helper <../helpers/form_helper>` functions will +automatically perform HTML escaping when necessary. + +Therefore, the *prep_for_form* method/rule is pretty much useless and is now +deprecated and scheduled for removal in 3.1+. + +.. note:: The method is still available, but you're strongly encouraged to + remove its usage sooner rather than later. + *********************************************************** Step 21: Check your usage of Text helper highlight_phrase() *********************************************************** diff --git a/user_guide_src/source/installation/upgrade_310.rst b/user_guide_src/source/installation/upgrade_310.rst index 36f69d01b..cae814327 100644 --- a/user_guide_src/source/installation/upgrade_310.rst +++ b/user_guide_src/source/installation/upgrade_310.rst @@ -1,5 +1,5 @@ ############################# -Upgrading from 3.0.x to 3.1.x +Upgrading from 3.0.6 to 3.1.0 ############################# Before performing an update you should take your site offline by @@ -13,100 +13,26 @@ Replace all files and directories in your *system/* directory. .. note:: If you have any custom developed files in these directories, please make copies of them first. -Step 2: Change database connection handling -=========================================== +Step 2: Check your PHP version +============================== -"Loading" a database, whether by using the *config/autoload.php* settings -or manually via calling ``$this->load->database()`` or the less-known -``DB()`` function, will now throw a ``RuntimeException`` in case of a -failure. +We recommend always running versions that are `currently supported +<https://secure.php.net/supported-versions.php>`_, which right now is at least PHP 5.6. -In addition, being unable to set the configured character set is now also -considered a connection failure. +PHP 5.2.x versions are now officially not supported by CodeIgniter, and while 5.3.7+ +may be at least runnable, we strongly discourage you from using any PHP versions below +the ones listed on the `PHP.net Supported Versions <https://secure.php.net/supported-versions.php>`_ +page. -.. note:: This has been the case for most database drivers in the in the - past as well (i.e. all but the 'mysql', 'mysqli' and 'postgre' - drivers). +Step 3: If you're using the 'odbc' database driver, check for usage of Query Builder +==================================================================================== -What this means is that if you're unable to connect to a database, or -have an erroneous character set configured, CodeIgniter will no longer -fail silently, but will throw an exception instead. +:doc:`Query Builder <../database/query_builder>` functionality and ``escape()`` can +no longer be used with the 'odbc' database driver. -You may choose to explicitly catch it (and for that purpose you can't use -*config/autoload.php* to load the :doc:`Database Class <../database/index>`) -:: +This is because, due to its nature, the `ODBC extension for PHP <https://secure.php.net/odbc>`_ +does not provide a function that allows to safely escape user-supplied strings for usage +inside an SQL query (which our :doc:`Query Builder <../database/query_builder>` relies on). - try - { - $this->load->database(); - } - catch (RuntimeException $e) - { - // Handle the failure - } - -Or you may leave it to CodeIgniter's default exception handler, which would -log the error message and display an error screen if you're running in -development mode. - -Remove db_set_charset() calls ------------------------------ - -With the above-mentioned changes, the purpose of the ``db_set_charset()`` -method would now only be to change the connection character set at runtime. -That doesn't make sense and that's the reason why most database drivers -don't support it at all. -Thus, ``db_set_charset()`` is no longer necessary and is removed. - -Step 3: Check logic related to URI parsing of CLI requests -========================================================== - -When running a CodeIgniter application from the CLI, the -:doc:`URI Library <../libraries/uri>` will now ignore the -``$config['url_suffix']`` and ``$config['permitted_uri_chars']`` -configuration settings. - -These two options don't make sense under the command line (which is why -this change was made) and therefore you shouldn't be affected by this, but -if you've relied on them for some reason, you'd probably have to make some -changes to your code. - -Step 4: Check Cache Library configurations for Redis, Memcache(d) -================================================================= - -The new improvements for the 'redis' and 'memcached' drivers of the -:doc:`Cache Library <../libraries/caching>` may require some small -adjustments to your configuration values ... - -Redis ------ - -If you're using the 'redis' driver with a UNIX socket connection, you'll -have to move the socket path from ``$config['socket']`` to -``$config['host']`` instead. - -The ``$config['socket_type']`` option is also removed, although that won't -affect your application - it will be ignored and the connection type will -be determined by the format used for ``$config['host']`` instead. - -Memcache(d) ------------ - -The 'memcached' will now ignore configurations that don't specify a ``host`` -value (previously, it just set the host to the default '127.0.0.1'). - -Therefore, if you've added a configuration that only sets e.g. a ``port``, -you will now have to explicitly set the ``host`` to '127.0.0.1' as well. - -Step 5: Check usage of doctype() HTML helper -============================================ - -The :doc:`HTML Helper <../helpers/html_helper>` function -:php:func:`doctype()` used to default to 'xhtml1-strict' (XHTML 1.0 Strict) -when no document type was specified. That default value is now changed to -'html5', which obviously stands for the modern HTML 5 standard. - -Nothing should be really broken by this change, but if your application -relies on the default value, you should double-check it and either -explicitly set the desired format, or adapt your front-end to use proper -HTML 5 formatting.
\ No newline at end of file +Thus, user inputs MUST be bound, as shown in :doc:`Running Queries <../database/queries>`, +under the "Query Bindings" section. diff --git a/user_guide_src/source/installation/upgrade_307.rst b/user_guide_src/source/installation/upgrade_311.rst index ee957aabf..a36e72323 100644 --- a/user_guide_src/source/installation/upgrade_307.rst +++ b/user_guide_src/source/installation/upgrade_311.rst @@ -1,5 +1,5 @@ ############################# -Upgrading from 3.0.6 to 3.0.7 +Upgrading from 3.1.0 to 3.1.1 ############################# Before performing an update you should take your site offline by diff --git a/user_guide_src/source/installation/upgrade_320.rst b/user_guide_src/source/installation/upgrade_320.rst new file mode 100644 index 000000000..942a8a3b5 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_320.rst @@ -0,0 +1,112 @@ +############################# +Upgrading from 3.1.x to 3.2.x +############################# + +Before performing an update you should take your site offline by +replacing the index.php file with a static one. + +Step 1: Update your CodeIgniter files +===================================== + +Replace all files and directories in your *system/* directory. + +.. note:: If you have any custom developed files in these directories, + please make copies of them first. + +Step 2: Change database connection handling +=========================================== + +"Loading" a database, whether by using the *config/autoload.php* settings +or manually via calling ``$this->load->database()`` or the less-known +``DB()`` function, will now throw a ``RuntimeException`` in case of a +failure. + +In addition, being unable to set the configured character set is now also +considered a connection failure. + +.. note:: This has been the case for most database drivers in the in the + past as well (i.e. all but the 'mysql', 'mysqli' and 'postgre' + drivers). + +What this means is that if you're unable to connect to a database, or +have an erroneous character set configured, CodeIgniter will no longer +fail silently, but will throw an exception instead. + +You may choose to explicitly catch it (and for that purpose you can't use +*config/autoload.php* to load the :doc:`Database Class <../database/index>`) +:: + + try + { + $this->load->database(); + } + catch (RuntimeException $e) + { + // Handle the failure + } + +Or you may leave it to CodeIgniter's default exception handler, which would +log the error message and display an error screen if you're running in +development mode. + +Remove db_set_charset() calls +----------------------------- + +With the above-mentioned changes, the purpose of the ``db_set_charset()`` +method would now only be to change the connection character set at runtime. +That doesn't make sense and that's the reason why most database drivers +don't support it at all. +Thus, ``db_set_charset()`` is no longer necessary and is removed. + +Step 3: Check logic related to URI parsing of CLI requests +========================================================== + +When running a CodeIgniter application from the CLI, the +:doc:`URI Library <../libraries/uri>` will now ignore the +``$config['url_suffix']`` and ``$config['permitted_uri_chars']`` +configuration settings. + +These two options don't make sense under the command line (which is why +this change was made) and therefore you shouldn't be affected by this, but +if you've relied on them for some reason, you'd probably have to make some +changes to your code. + +Step 4: Check Cache Library configurations for Redis, Memcache(d) +================================================================= + +The new improvements for the 'redis' and 'memcached' drivers of the +:doc:`Cache Library <../libraries/caching>` may require some small +adjustments to your configuration values ... + +Redis +----- + +If you're using the 'redis' driver with a UNIX socket connection, you'll +have to move the socket path from ``$config['socket']`` to +``$config['host']`` instead. + +The ``$config['socket_type']`` option is also removed, although that won't +affect your application - it will be ignored and the connection type will +be determined by the format used for ``$config['host']`` instead. + +Memcache(d) +----------- + +The 'memcached' will now ignore configurations that don't specify a ``host`` +value (previously, it just set the host to the default '127.0.0.1'). + +Therefore, if you've added a configuration that only sets e.g. a ``port``, +you will now have to explicitly set the ``host`` to '127.0.0.1' as well. + +Step 5: Check usage of doctype() HTML helper +============================================ + +The :doc:`HTML Helper <../helpers/html_helper>` function +:php:func:`doctype()` used to default to 'xhtml1-strict' (XHTML 1.0 Strict) +when no document type was specified. That default value is now changed to +'html5', which obviously stands for the modern HTML 5 standard. + +Nothing should be really broken by this change, but if your application +relies on the default value, you should double-check it and either +explicitly set the desired format, or adapt your front-end to use proper +HTML 5 formatting. diff --git a/user_guide_src/source/installation/upgrading.rst b/user_guide_src/source/installation/upgrading.rst index f42db7be5..01812169b 100644 --- a/user_guide_src/source/installation/upgrading.rst +++ b/user_guide_src/source/installation/upgrading.rst @@ -8,8 +8,9 @@ upgrading from. .. toctree:: :titlesonly: - Upgrading from 3.0.x to 3.1.x <upgrade_310> - Upgrading from 3.0.6 to 3.0.7 <upgrade_307> + Upgrading from 3.1.x to 3.2.x <upgrade_320> + Upgrading from 3.1.0 to 3.1.1 <upgrade_311> + Upgrading from 3.0.6 to 3.1.0 <upgrade_310> Upgrading from 3.0.5 to 3.0.6 <upgrade_306> Upgrading from 3.0.4 to 3.0.5 <upgrade_305> Upgrading from 3.0.3 to 3.0.4 <upgrade_304> diff --git a/user_guide_src/source/libraries/form_validation.rst b/user_guide_src/source/libraries/form_validation.rst index 44adfd715..b03b544e2 100644 --- a/user_guide_src/source/libraries/form_validation.rst +++ b/user_guide_src/source/libraries/form_validation.rst @@ -465,7 +465,7 @@ for you to process. To invoke a callback just put the method name in a rule, with "callback\_" as the rule **prefix**. If you need to receive an extra parameter in your callback method, just add it normally after the -method name between square brackets, as in: "callback_foo**[bar]**", +method name between square brackets, as in: ``callback_foo[bar]``, then it will be passed as the second argument of your callback method. .. note:: You can also process the form data that is passed to your |