diff options
author | DiederikLascaris <info@runesa.nl> | 2017-01-22 21:45:15 +0100 |
---|---|---|
committer | DiederikLascaris <info@runesa.nl> | 2017-01-22 21:45:15 +0100 |
commit | 312efeba32d532878c536fd28e75f39d61a9dade (patch) | |
tree | 71ae4aaca33a9d34a789d6d375acd2594160123a /system/libraries | |
parent | 947f1a06093a52a32b703ee795e52c22cd66363a (diff) | |
parent | 44c7af639ac1726780b64fb5a6cb6fca2df8b651 (diff) |
Merge branch 'develop' of git://github.com/bcit-ci/CodeIgniter into develop
Diffstat (limited to 'system/libraries')
39 files changed, 1536 insertions, 3579 deletions
diff --git a/system/libraries/Cache/Cache.php b/system/libraries/Cache/Cache.php index 0c87a5628..267dffb09 100644 --- a/system/libraries/Cache/Cache.php +++ b/system/libraries/Cache/Cache.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 2.0.0 * @filesource */ diff --git a/system/libraries/Cache/drivers/Cache_apc.php b/system/libraries/Cache/drivers/Cache_apc.php index e0d2ffb39..f2b61adb1 100644 --- a/system/libraries/Cache/drivers/Cache_apc.php +++ b/system/libraries/Cache/drivers/Cache_apc.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 2.0.0 * @filesource */ @@ -49,6 +49,24 @@ defined('BASEPATH') OR exit('No direct script access allowed'); class CI_Cache_apc extends CI_Driver { /** + * Class constructor + * + * Only present so that an error message is logged + * if APC is not available. + * + * @return void + */ + public function __construct() + { + if ( ! $this->is_supported()) + { + log_message('error', 'Cache: Failed to initialize APC; extension not loaded/enabled?'); + } + } + + // ------------------------------------------------------------------------ + + /** * Get * * Look for a value in the cache. If it exists, return the data @@ -79,7 +97,7 @@ class CI_Cache_apc extends CI_Driver { * * @param string $id Cache ID * @param mixed $data Data to store - * @param int $ttol Length of time (in seconds) to cache the data + * @param int $ttl Length of time (in seconds) to cache the data * @param bool $raw Whether to store the raw value * @return bool TRUE on success, FALSE on failure */ @@ -198,13 +216,6 @@ class CI_Cache_apc extends CI_Driver { */ public function is_supported() { - if ( ! extension_loaded('apc') OR ! ini_get('apc.enabled')) - { - log_message('debug', 'The APC PHP extension must be loaded to use APC Cache.'); - return FALSE; - } - - return TRUE; + return (extension_loaded('apc') && ini_get('apc.enabled')); } - } diff --git a/system/libraries/Cache/drivers/Cache_dummy.php b/system/libraries/Cache/drivers/Cache_dummy.php index bf80945a9..c6d9a61f1 100644 --- a/system/libraries/Cache/drivers/Cache_dummy.php +++ b/system/libraries/Cache/drivers/Cache_dummy.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 2.0 * @filesource */ diff --git a/system/libraries/Cache/drivers/Cache_file.php b/system/libraries/Cache/drivers/Cache_file.php index 68bc1ec96..8a36e9d79 100644 --- a/system/libraries/Cache/drivers/Cache_file.php +++ b/system/libraries/Cache/drivers/Cache_file.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 2.0 * @filesource */ @@ -120,7 +120,7 @@ class CI_Cache_file extends CI_Driver { */ public function delete($id) { - return file_exists($this->_cache_path.$id) ? unlink($this->_cache_path.$id) : FALSE; + return is_file($this->_cache_path.$id) ? unlink($this->_cache_path.$id) : FALSE; } // ------------------------------------------------------------------------ @@ -216,7 +216,7 @@ class CI_Cache_file extends CI_Driver { */ public function get_metadata($id) { - if ( ! file_exists($this->_cache_path.$id)) + if ( ! is_file($this->_cache_path.$id)) { return FALSE; } @@ -227,13 +227,13 @@ class CI_Cache_file extends CI_Driver { { $mtime = filemtime($this->_cache_path.$id); - if ( ! isset($data['ttl'])) + if ( ! isset($data['ttl'], $data['time'])) { return FALSE; } return array( - 'expire' => $mtime + $data['ttl'], + 'expire' => $data['time'] + $data['ttl'], 'mtime' => $mtime ); } @@ -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 111e2109d..4836b6aed 100644 --- a/system/libraries/Cache/drivers/Cache_memcached.php +++ b/system/libraries/Cache/drivers/Cache_memcached.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 2.0 * @filesource */ @@ -60,7 +60,7 @@ class CI_Cache_memcached extends CI_Driver { * * @var array */ - protected $_memcache_conf = array( + protected $_config = array( 'default' => array( 'host' => '127.0.0.1', 'port' => 11211, @@ -81,19 +81,11 @@ class CI_Cache_memcached extends CI_Driver { { // Try to load memcached server info from the config file. $CI =& get_instance(); - $defaults = $this->_memcache_conf['default']; + $defaults = $this->_config['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; - } - } + $this->_config = $CI->config->config['memcached']; } if (class_exists('Memcached', FALSE)) @@ -106,18 +98,31 @@ class CI_Cache_memcached extends CI_Driver { } else { - throw new RuntimeException('Cache: Failed to create Memcache(d) object; extension not loaded?'); + log_message('error', 'Cache: Failed to create Memcache(d) object; extension not loaded?'); + return; } - foreach ($this->_memcache_conf as $cache_server) + foreach ($this->_config as $cache_name => $cache_server) { - isset($cache_server['hostname']) OR $cache_server['hostname'] = $defaults['host']; - isset($cache_server['port']) OR $cache_server['port'] = $defaults['port']; + if ( ! isset($cache_server['hostname'])) + { + log_message('debug', 'Cache: Memcache(d) configuration "'.$cache_name.'" doesn\'t include a hostname; ignoring.'); + continue; + } + elseif ($cache_server['hostname'][0] === '/') + { + $cache_server['port'] = 0; + } + elseif (empty($cache_server['port'])) + { + $cache_server['port'] = $defaults['port']; + } + isset($cache_server['weight']) OR $cache_server['weight'] = $defaults['weight']; - if (get_class($this->_memcached) === 'Memcache') + if ($this->_memcached instanceof Memcache) { - // Third parameter is persistance and defaults to TRUE. + // Third parameter is persistence and defaults to TRUE. $this->_memcached->addServer( $cache_server['hostname'], $cache_server['port'], @@ -125,7 +130,7 @@ class CI_Cache_memcached extends CI_Driver { $cache_server['weight'] ); } - else + elseif ($this->_memcached instanceof Memcached) { $this->_memcached->addServer( $cache_server['hostname'], @@ -169,11 +174,11 @@ class CI_Cache_memcached extends CI_Driver { $data = array($data, time(), $ttl); } - if (get_class($this->_memcached) === 'Memcached') + if ($this->_memcached instanceof Memcached) { return $this->_memcached->set($id, $data, $ttl); } - elseif (get_class($this->_memcached) === 'Memcache') + elseif ($this->_memcached instanceof Memcache) { return $this->_memcached->set($id, $data, 0, $ttl); } @@ -186,7 +191,7 @@ class CI_Cache_memcached extends CI_Driver { /** * Delete from Cache * - * @param mixed key to be deleted. + * @param mixed $id key to be deleted. * @return bool true on success, false on failure */ public function delete($id) @@ -251,7 +256,7 @@ class CI_Cache_memcached extends CI_Driver { /** * Get Cache Metadata * - * @param mixed key to get cache metadata on + * @param mixed $id key to get cache metadata on * @return mixed FALSE on failure, array on success. */ public function get_metadata($id) @@ -284,12 +289,27 @@ class CI_Cache_memcached extends CI_Driver { */ public function is_supported() { - if ( ! extension_loaded('memcached') && ! extension_loaded('memcache')) + return (extension_loaded('memcached') OR extension_loaded('memcache')); + } + + // ------------------------------------------------------------------------ + + /** + * Class destructor + * + * Closes the connection to Memcache(d) if present. + * + * @return void + */ + public function __destruct() + { + if ($this->_memcached instanceof Memcache) { - log_message('debug', 'The Memcached Extension must be loaded to use Memcached Cache.'); - return FALSE; + $this->_memcached->close(); + } + elseif ($this->_memcached instanceof Memcached && method_exists($this->_memcached, 'quit')) + { + $this->_memcached->quit(); } - - return TRUE; } } diff --git a/system/libraries/Cache/drivers/Cache_redis.php b/system/libraries/Cache/drivers/Cache_redis.php index d7dca1973..bb26b3b47 100644 --- a/system/libraries/Cache/drivers/Cache_redis.php +++ b/system/libraries/Cache/drivers/Cache_redis.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ @@ -55,11 +55,11 @@ class CI_Cache_redis extends CI_Driver * @var array */ protected static $_default_config = array( - 'socket_type' => 'tcp', 'host' => '127.0.0.1', 'password' => NULL, 'port' => 6379, - 'timeout' => 0 + 'timeout' => 0, + 'database' => 0 ); /** @@ -69,13 +69,6 @@ class CI_Cache_redis extends CI_Driver */ protected $_redis; - /** - * An internal cache for storing keys of serialized values. - * - * @var array - */ - protected $_serialized = array(); - // ------------------------------------------------------------------------ /** @@ -91,46 +84,46 @@ class CI_Cache_redis extends CI_Driver */ public function __construct() { - $config = array(); + if ( ! $this->is_supported()) + { + log_message('error', 'Cache: Failed to create Redis object; extension not loaded?'); + return; + } + $CI =& get_instance(); if ($CI->config->load('redis', TRUE, TRUE)) { - $config = $CI->config->item('redis'); + $config = array_merge(self::$_default_config, $CI->config->item('redis')); + } + else + { + $config = self::$_default_config; } - $config = array_merge(self::$_default_config, $config); $this->_redis = new Redis(); try { - if ($config['socket_type'] === 'unix') + if ( ! $this->_redis->connect($config['host'], ($config['host'][0] === '/' ? 0 : $config['port']), $config['timeout'])) { - $success = $this->_redis->connect($config['socket']); + log_message('error', 'Cache: Redis connection failed. Check your configuration.'); } - else // tcp socket + + if (isset($config['password']) && ! $this->_redis->auth($config['password'])) { - $success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']); + log_message('error', 'Cache: Redis authentication failed.'); } - if ( ! $success) + if (isset($config['database']) && $config['database'] > 0 && ! $this->_redis->select($config['database'])) { - throw new RuntimeException('Cache: Redis connection failed. Check your configuration.'); + log_message('error', 'Cache: Redis select database failed.'); } } catch (RedisException $e) { - throw new RuntimeException('Cache: Redis connection refused ('.$e->getMessage().')'); + log_message('error', 'Cache: Redis connection refused ('.$e->getMessage().')'); } - - if (isset($config['password']) && ! $this->_redis->auth($config['password'])) - { - throw new RuntimeException('Cache: Redis authentication failed.'); - } - - // Initialize the index of serialized values. - $serialized = $this->_redis->sMembers('_ci_redis_serialized'); - empty($serialized) OR $this->_serialized = array_flip($serialized); } // ------------------------------------------------------------------------ @@ -138,19 +131,35 @@ class CI_Cache_redis extends CI_Driver /** * Get cache * - * @param string Cache ID + * @param string $key Cache ID * @return mixed */ public function get($key) { - $value = $this->_redis->get($key); + $data = $this->_redis->hMGet($key, array('__ci_type', '__ci_value')); - if ($value !== FALSE && isset($this->_serialized[$key])) + if ( ! isset($data['__ci_type'], $data['__ci_value']) OR $data['__ci_value'] === FALSE) { - return unserialize($value); + return FALSE; } - return $value; + switch ($data['__ci_type']) + { + case 'array': + case 'object': + return unserialize($data['__ci_value']); + case 'boolean': + case 'integer': + case 'double': // Yes, 'double' is returned and NOT 'float' + case 'string': + case 'NULL': + return settype($data['__ci_value'], $data['__ci_type']) + ? $data['__ci_value'] + : FALSE; + case 'resource': + default: + return FALSE; + } } // ------------------------------------------------------------------------ @@ -166,23 +175,33 @@ class CI_Cache_redis extends CI_Driver */ public function save($id, $data, $ttl = 60, $raw = FALSE) { - if (is_array($data) OR is_object($data)) + switch ($data_type = gettype($data)) { - if ( ! $this->_redis->sIsMember('_ci_redis_serialized', $id) && ! $this->_redis->sAdd('_ci_redis_serialized', $id)) - { + case 'array': + case 'object': + $data = serialize($data); + break; + case 'boolean': + case 'integer': + case 'double': // Yes, 'double' is returned and NOT 'float' + case 'string': + case 'NULL': + break; + case 'resource': + default: return FALSE; - } + } - isset($this->_serialized[$id]) OR $this->_serialized[$id] = TRUE; - $data = serialize($data); + if ( ! $this->_redis->hMSet($id, array('__ci_type' => $data_type, '__ci_value' => $data))) + { + return FALSE; } - elseif (isset($this->_serialized[$id])) + elseif ($ttl) { - $this->_serialized[$id] = NULL; - $this->_redis->sRemove('_ci_redis_serialized', $id); + $this->_redis->expireAt($id, time() + $ttl); } - return $this->_redis->set($id, $data, $ttl); + return TRUE; } // ------------------------------------------------------------------------ @@ -190,23 +209,12 @@ class CI_Cache_redis extends CI_Driver /** * Delete from cache * - * @param string Cache key + * @param string $key Cache key * @return bool */ public function delete($key) { - if ($this->_redis->delete($key) !== 1) - { - return FALSE; - } - - if (isset($this->_serialized[$key])) - { - $this->_serialized[$key] = NULL; - $this->_redis->sRemove('_ci_redis_serialized', $key); - } - - return TRUE; + return ($this->_redis->delete($key) === 1); } // ------------------------------------------------------------------------ @@ -220,7 +228,7 @@ class CI_Cache_redis extends CI_Driver */ public function increment($id, $offset = 1) { - return $this->_redis->incr($id, $offset); + return $this->_redis->hIncrBy($id, 'data', $offset); } // ------------------------------------------------------------------------ @@ -234,7 +242,7 @@ class CI_Cache_redis extends CI_Driver */ public function decrement($id, $offset = 1) { - return $this->_redis->decr($id, $offset); + return $this->_redis->hIncrBy($id, 'data', -$offset); } // ------------------------------------------------------------------------ @@ -255,9 +263,9 @@ class CI_Cache_redis extends CI_Driver /** * Get cache driver info * - * @param string Not supported in Redis. - * Only included in order to offer a - * consistent cache API. + * @param string $type Not supported in Redis. + * Only included in order to offer a + * consistent cache API. * @return array * @see Redis::info() */ @@ -271,7 +279,7 @@ class CI_Cache_redis extends CI_Driver /** * Get cache metadata * - * @param string Cache key + * @param string $key Cache key * @return array */ public function get_metadata($key) @@ -298,13 +306,7 @@ class CI_Cache_redis extends CI_Driver */ public function is_supported() { - if ( ! extension_loaded('redis')) - { - log_message('debug', 'The Redis extension must be loaded to use Redis cache.'); - return FALSE; - } - - return TRUE; + return extension_loaded('redis'); } // ------------------------------------------------------------------------ diff --git a/system/libraries/Cache/drivers/Cache_wincache.php b/system/libraries/Cache/drivers/Cache_wincache.php index 9cc6ff016..f296a5e26 100644 --- a/system/libraries/Cache/drivers/Cache_wincache.php +++ b/system/libraries/Cache/drivers/Cache_wincache.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ @@ -52,6 +52,24 @@ defined('BASEPATH') OR exit('No direct script access allowed'); class CI_Cache_wincache extends CI_Driver { /** + * Class constructor + * + * Only present so that an error message is logged + * if APC is not available. + * + * @return void + */ + public function __construct() + { + if ( ! $this->is_supported()) + { + log_message('error', 'Cache: Failed to initialize Wincache; extension not loaded/enabled?'); + } + } + + // ------------------------------------------------------------------------ + + /** * Get * * Look for a value in the cache. If it exists, return the data, @@ -194,13 +212,6 @@ class CI_Cache_wincache extends CI_Driver { */ public function is_supported() { - 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; - } - - return TRUE; + return (extension_loaded('wincache') && ini_get('wincache.ucenabled')); } - } diff --git a/system/libraries/Calendar.php b/system/libraries/Calendar.php index f6a0c39c4..edb0fb4d9 100644 --- a/system/libraries/Calendar.php +++ b/system/libraries/Calendar.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -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/calendar.html + * @link https://codeigniter.com/user_guide/libraries/calendar.html */ class CI_Calendar { diff --git a/system/libraries/Cart.php b/system/libraries/Cart.php deleted file mode 100644 index bf27c6392..000000000 --- a/system/libraries/Cart.php +++ /dev/null @@ -1,567 +0,0 @@ -<?php -/** - * CodeIgniter - * - * An open source application development framework for PHP - * - * This content is released under the MIT License (MIT) - * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @package CodeIgniter - * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) - * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com - * @since Version 1.0.0 - * @filesource - */ -defined('BASEPATH') OR exit('No direct script access allowed'); - -/** - * Shopping Cart Class - * - * @package CodeIgniter - * @subpackage Libraries - * @category Shopping Cart - * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/cart.html - * @deprecated 3.0.0 This class is too specific for CI. - */ -class CI_Cart { - - /** - * These are the regular expression rules that we use to validate the product ID and product name - * alpha-numeric, dashes, underscores, or periods - * - * @var string - */ - public $product_id_rules = '\.a-z0-9_-'; - - /** - * These are the regular expression rules that we use to validate the product ID and product name - * alpha-numeric, dashes, underscores, colons or periods - * - * @var string - */ - public $product_name_rules = '\w \-\.\:'; - - /** - * only allow safe product names - * - * @var bool - */ - public $product_name_safe = TRUE; - - // -------------------------------------------------------------------------- - - /** - * Reference to CodeIgniter instance - * - * @var object - */ - protected $CI; - - /** - * Contents of the cart - * - * @var array - */ - protected $_cart_contents = array(); - - /** - * Shopping Class Constructor - * - * The constructor loads the Session class, used to store the shopping cart contents. - * - * @param array - * @return void - */ - public function __construct($params = array()) - { - // Set the super object to a local variable for use later - $this->CI =& get_instance(); - - // Are any config settings being passed manually? If so, set them - $config = is_array($params) ? $params : array(); - - // Load the Sessions class - $this->CI->load->driver('session', $config); - - // Grab the shopping cart array from the session table - $this->_cart_contents = $this->CI->session->userdata('cart_contents'); - if ($this->_cart_contents === NULL) - { - // No cart exists so we'll set some base values - $this->_cart_contents = array('cart_total' => 0, 'total_items' => 0); - } - - log_message('info', 'Cart Class Initialized'); - } - - // -------------------------------------------------------------------- - - /** - * Insert items into the cart and save it to the session table - * - * @param array - * @return bool - */ - public function insert($items = array()) - { - // Was any cart data passed? No? Bah... - if ( ! is_array($items) OR count($items) === 0) - { - log_message('error', 'The insert method must be passed an array containing data.'); - return FALSE; - } - - // You can either insert a single product using a one-dimensional array, - // or multiple products using a multi-dimensional one. The way we - // determine the array type is by looking for a required array key named "id" - // at the top level. If it's not found, we will assume it's a multi-dimensional array. - - $save_cart = FALSE; - if (isset($items['id'])) - { - if (($rowid = $this->_insert($items))) - { - $save_cart = TRUE; - } - } - else - { - foreach ($items as $val) - { - if (is_array($val) && isset($val['id'])) - { - if ($this->_insert($val)) - { - $save_cart = TRUE; - } - } - } - } - - // Save the cart data if the insert was successful - if ($save_cart === TRUE) - { - $this->_save_cart(); - return isset($rowid) ? $rowid : TRUE; - } - - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Insert - * - * @param array - * @return bool - */ - protected function _insert($items = array()) - { - // Was any cart data passed? No? Bah... - if ( ! is_array($items) OR count($items) === 0) - { - log_message('error', 'The insert method must be passed an array containing data.'); - return FALSE; - } - - // -------------------------------------------------------------------- - - // Does the $items array contain an id, quantity, price, and name? These are required - if ( ! isset($items['id'], $items['qty'], $items['price'], $items['name'])) - { - log_message('error', 'The cart array must contain a product ID, quantity, price, and name.'); - return FALSE; - } - - // -------------------------------------------------------------------- - - // Prep the quantity. It can only be a number. Duh... also trim any leading zeros - $items['qty'] = (float) $items['qty']; - - // If the quantity is zero or blank there's nothing for us to do - if ($items['qty'] == 0) - { - return FALSE; - } - - // -------------------------------------------------------------------- - - // Validate the product ID. It can only be alpha-numeric, dashes, underscores or periods - // Not totally sure we should impose this rule, but it seems prudent to standardize IDs. - // Note: These can be user-specified by setting the $this->product_id_rules variable. - if ( ! preg_match('/^['.$this->product_id_rules.']+$/i', $items['id'])) - { - log_message('error', 'Invalid product ID. The product ID can only contain alpha-numeric characters, dashes, and underscores'); - return FALSE; - } - - // -------------------------------------------------------------------- - - // Validate the product name. It can only be alpha-numeric, dashes, underscores, colons or periods. - // Note: These can be user-specified by setting the $this->product_name_rules variable. - if ($this->product_name_safe && ! preg_match('/^['.$this->product_name_rules.']+$/i'.(UTF8_ENABLED ? 'u' : ''), $items['name'])) - { - log_message('error', 'An invalid name was submitted as the product name: '.$items['name'].' The name can only contain alpha-numeric characters, dashes, underscores, colons, and spaces'); - return FALSE; - } - - // -------------------------------------------------------------------- - - // Prep the price. Remove leading zeros and anything that isn't a number or decimal point. - $items['price'] = (float) $items['price']; - - // We now need to create a unique identifier for the item being inserted into the cart. - // Every time something is added to the cart it is stored in the master cart array. - // Each row in the cart array, however, must have a unique index that identifies not only - // a particular product, but makes it possible to store identical products with different options. - // For example, what if someone buys two identical t-shirts (same product ID), but in - // different sizes? The product ID (and other attributes, like the name) will be identical for - // both sizes because it's the same shirt. The only difference will be the size. - // Internally, we need to treat identical submissions, but with different options, as a unique product. - // Our solution is to convert the options array to a string and MD5 it along with the product ID. - // This becomes the unique "row ID" - if (isset($items['options']) && count($items['options']) > 0) - { - $rowid = md5($items['id'].serialize($items['options'])); - } - else - { - // No options were submitted so we simply MD5 the product ID. - // Technically, we don't need to MD5 the ID in this case, but it makes - // sense to standardize the format of array indexes for both conditions - $rowid = md5($items['id']); - } - - // -------------------------------------------------------------------- - - // Now that we have our unique "row ID", we'll add our cart items to the master array - // grab quantity if it's already there and add it on - $old_quantity = isset($this->_cart_contents[$rowid]['qty']) ? (int) $this->_cart_contents[$rowid]['qty'] : 0; - - // Re-create the entry, just to make sure our index contains only the data from this submission - $items['rowid'] = $rowid; - $items['qty'] += $old_quantity; - $this->_cart_contents[$rowid] = $items; - - return $rowid; - } - - // -------------------------------------------------------------------- - - /** - * Update the cart - * - * This function permits the quantity of a given item to be changed. - * Typically it is called from the "view cart" page if a user makes - * changes to the quantity before checkout. That array must contain the - * product ID and quantity for each item. - * - * @param array - * @return bool - */ - public function update($items = array()) - { - // Was any cart data passed? - if ( ! is_array($items) OR count($items) === 0) - { - return FALSE; - } - - // You can either update a single product using a one-dimensional array, - // or multiple products using a multi-dimensional one. The way we - // determine the array type is by looking for a required array key named "rowid". - // If it's not found we assume it's a multi-dimensional array - $save_cart = FALSE; - if (isset($items['rowid'])) - { - if ($this->_update($items) === TRUE) - { - $save_cart = TRUE; - } - } - else - { - foreach ($items as $val) - { - if (is_array($val) && isset($val['rowid'])) - { - if ($this->_update($val) === TRUE) - { - $save_cart = TRUE; - } - } - } - } - - // Save the cart data if the insert was successful - if ($save_cart === TRUE) - { - $this->_save_cart(); - return TRUE; - } - - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Update the cart - * - * This function permits changing item properties. - * Typically it is called from the "view cart" page if a user makes - * changes to the quantity before checkout. That array must contain the - * rowid and quantity for each item. - * - * @param array - * @return bool - */ - protected function _update($items = array()) - { - // Without these array indexes there is nothing we can do - if ( ! isset($items['rowid'], $this->_cart_contents[$items['rowid']])) - { - return FALSE; - } - - // Prep the quantity - if (isset($items['qty'])) - { - $items['qty'] = (float) $items['qty']; - // Is the quantity zero? If so we will remove the item from the cart. - // If the quantity is greater than zero we are updating - if ($items['qty'] == 0) - { - unset($this->_cart_contents[$items['rowid']]); - return TRUE; - } - } - - // find updatable keys - $keys = array_intersect(array_keys($this->_cart_contents[$items['rowid']]), array_keys($items)); - // if a price was passed, make sure it contains valid data - if (isset($items['price'])) - { - $items['price'] = (float) $items['price']; - } - - // product id & name shouldn't be changed - foreach (array_diff($keys, array('id', 'name')) as $key) - { - $this->_cart_contents[$items['rowid']][$key] = $items[$key]; - } - - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Save the cart array to the session DB - * - * @return bool - */ - protected function _save_cart() - { - // Let's add up the individual prices and set the cart sub-total - $this->_cart_contents['total_items'] = $this->_cart_contents['cart_total'] = 0; - foreach ($this->_cart_contents as $key => $val) - { - // We make sure the array contains the proper indexes - if ( ! is_array($val) OR ! isset($val['price'], $val['qty'])) - { - continue; - } - - $this->_cart_contents['cart_total'] += ($val['price'] * $val['qty']); - $this->_cart_contents['total_items'] += $val['qty']; - $this->_cart_contents[$key]['subtotal'] = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']); - } - - // Is our cart empty? If so we delete it from the session - if (count($this->_cart_contents) <= 2) - { - $this->CI->session->unset_userdata('cart_contents'); - - // Nothing more to do... coffee time! - return FALSE; - } - - // If we made it this far it means that our cart has data. - // Let's pass it to the Session class so it can be stored - $this->CI->session->set_userdata(array('cart_contents' => $this->_cart_contents)); - - // Woot! - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Cart Total - * - * @return int - */ - public function total() - { - return $this->_cart_contents['cart_total']; - } - - // -------------------------------------------------------------------- - - /** - * Remove Item - * - * Removes an item from the cart - * - * @param int - * @return bool - */ - public function remove($rowid) - { - // unset & save - unset($this->_cart_contents[$rowid]); - $this->_save_cart(); - return TRUE; - } - - // -------------------------------------------------------------------- - - /** - * Total Items - * - * Returns the total item count - * - * @return int - */ - public function total_items() - { - return $this->_cart_contents['total_items']; - } - - // -------------------------------------------------------------------- - - /** - * Cart Contents - * - * Returns the entire cart array - * - * @param bool - * @return array - */ - public function contents($newest_first = FALSE) - { - // do we want the newest first? - $cart = ($newest_first) ? array_reverse($this->_cart_contents) : $this->_cart_contents; - - // Remove these so they don't create a problem when showing the cart table - unset($cart['total_items']); - unset($cart['cart_total']); - - return $cart; - } - - // -------------------------------------------------------------------- - - /** - * Get cart item - * - * Returns the details of a specific item in the cart - * - * @param string $row_id - * @return array - */ - public function get_item($row_id) - { - return (in_array($row_id, array('total_items', 'cart_total'), TRUE) OR ! isset($this->_cart_contents[$row_id])) - ? FALSE - : $this->_cart_contents[$row_id]; - } - - // -------------------------------------------------------------------- - - /** - * Has options - * - * Returns TRUE if the rowid passed to this function correlates to an item - * that has options associated with it. - * - * @param string $row_id = '' - * @return bool - */ - public function has_options($row_id = '') - { - return (isset($this->_cart_contents[$row_id]['options']) && count($this->_cart_contents[$row_id]['options']) !== 0); - } - - // -------------------------------------------------------------------- - - /** - * Product options - * - * Returns the an array of options, for a particular product row ID - * - * @param string $row_id = '' - * @return array - */ - public function product_options($row_id = '') - { - return isset($this->_cart_contents[$row_id]['options']) ? $this->_cart_contents[$row_id]['options'] : array(); - } - - // -------------------------------------------------------------------- - - /** - * Format Number - * - * Returns the supplied number with commas and a decimal point. - * - * @param float - * @return string - */ - public function format_number($n = '') - { - return ($n === '') ? '' : number_format( (float) $n, 2, '.', ','); - } - - // -------------------------------------------------------------------- - - /** - * Destroy the cart - * - * Empties the cart and kills the session - * - * @return void - */ - public function destroy() - { - $this->_cart_contents = array('cart_total' => 0, 'total_items' => 0); - $this->CI->session->unset_userdata('cart_contents'); - } - -} diff --git a/system/libraries/Driver.php b/system/libraries/Driver.php index da4c548e6..00e8416f9 100644 --- a/system/libraries/Driver.php +++ b/system/libraries/Driver.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ diff --git a/system/libraries/Email.php b/system/libraries/Email.php index 459c8f590..1483f2203 100644 --- a/system/libraries/Email.php +++ b/system/libraries/Email.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -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/email.html + * @link https://codeigniter.com/user_guide/libraries/email.html */ class CI_Email { @@ -150,13 +150,6 @@ class CI_Email { public $charset = 'utf-8'; /** - * Multipart message - * - * @var string 'mixed' (in the body) or 'related' (separate) - */ - public $multipart = 'mixed'; // "mixed" (in the body) or "related" (separate) - - /** * Alternative message (for HTML messages only) * * @var string @@ -168,7 +161,7 @@ class CI_Email { * * @var bool */ - public $validate = FALSE; + public $validate = TRUE; /** * X-Priority header value. @@ -233,13 +226,6 @@ class CI_Email { // -------------------------------------------------------------------- /** - * Whether PHP is running in safe mode. Initialized by the class constructor. - * - * @var bool - */ - protected $_safe_mode = FALSE; - - /** * Subject header * * @var string @@ -261,20 +247,6 @@ class CI_Email { protected $_finalbody = ''; /** - * multipart/alternative boundary - * - * @var string - */ - protected $_alt_boundary = ''; - - /** - * Attachment boundary - * - * @var string - */ - protected $_atc_boundary = ''; - - /** * Final headers to send * * @var string @@ -395,6 +367,13 @@ class CI_Email { 5 => '5 (Lowest)' ); + /** + * mbstring.func_override flag + * + * @var bool + */ + protected static $func_override; + // -------------------------------------------------------------------- /** @@ -408,18 +387,9 @@ class CI_Email { public function __construct(array $config = array()) { $this->charset = config_item('charset'); + $this->initialize($config); - if (count($config) > 0) - { - $this->initialize($config); - } - else - { - $this->_smtp_auth = ! ($this->smtp_user === '' && $this->smtp_pass === ''); - } - - $this->_safe_mode = ( ! is_php('5.4') && ini_get('safe_mode')); - $this->charset = strtoupper($this->charset); + isset(self::$func_override) OR self::$func_override = (extension_loaded('mbstring') && ini_get('mbstring.func_override')); log_message('info', 'Email Class Initialized'); } @@ -427,28 +397,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)) @@ -465,9 +422,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; } @@ -493,7 +450,6 @@ class CI_Email { $this->_headers = array(); $this->_debug_msg = array(); - $this->set_header('User-Agent', $this->useragent); $this->set_header('Date', $this->_set_date()); if ($clear_attachments !== FALSE) @@ -574,14 +530,18 @@ class CI_Email { $this->validate_email($this->_str_to_array($replyto)); } - if ($name === '') - { - $name = $replyto; - } - - if (strpos($name, '"') !== 0) + if ($name !== '') { - $name = '"'.$name.'"'; + // only use Q encoding if there are characters that would require it + if ( ! preg_match('/[\200-\377]/', $name)) + { + // add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes + $name = '"'.addcslashes($name, "\0..\37\177'\"\\").'"'; + } + else + { + $name = $this->_prep_q_encoding($name); + } } $this->set_header('Reply-To', $name.' <'.$replyto.'>'); @@ -707,18 +667,6 @@ class CI_Email { public function message($body) { $this->_body = rtrim(str_replace("\r", '', $body)); - - /* strip slashes only if magic quotes is ON - if we do it with magic quotes OFF, it strips real, user-inputted chars. - - NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and - it will probably not exist in future versions at all. - */ - if ( ! is_php('5.4') && get_magic_quotes_gpc()) - { - $this->_body = stripslashes($this->_body); - } - return $this; } @@ -762,7 +710,8 @@ class CI_Email { 'name' => array($file, $newname), 'disposition' => empty($disposition) ? 'attachment' : $disposition, // Can also be 'inline' Not sure if it matters 'type' => $mime, - 'content' => chunk_split(base64_encode($file_content)) + 'content' => chunk_split(base64_encode($file_content)), + 'multipart' => 'mixed' ); return $this; @@ -780,15 +729,11 @@ class CI_Email { */ public function attachment_cid($filename) { - if ($this->multipart !== 'related') - { - $this->multipart = 'related'; // Thunderbird need this for inline images - } - for ($i = 0, $c = count($this->_attachments); $i < $c; $i++) { if ($this->_attachments[$i]['name'][0] === $filename) { + $this->_attachments[$i]['multipart'] = 'related'; $this->_attachments[$i]['cid'] = uniqid(basename($this->_attachments[$i]['name'][0]).'@'); return $this->_attachments[$i]['cid']; } @@ -933,19 +878,6 @@ class CI_Email { // -------------------------------------------------------------------- /** - * Set Message Boundary - * - * @return void - */ - protected function _set_boundaries() - { - $this->_alt_boundary = 'B_ALT_'.uniqid(''); // multipart/alternative - $this->_atc_boundary = 'B_ATC_'.uniqid(''); // attachment boundary - } - - // -------------------------------------------------------------------- - - /** * Get the Message ID * * @return string @@ -1012,9 +944,9 @@ class CI_Email { { if ($this->mailtype === 'html') { - return (count($this->_attachments) === 0) ? 'html' : 'html-attach'; + return empty($this->_attachments) ? 'html' : 'html-attach'; } - elseif ($this->mailtype === 'text' && count($this->_attachments) > 0) + elseif ($this->mailtype === 'text' && ! empty($this->_attachments)) { return 'plain-attach'; } @@ -1093,7 +1025,7 @@ class CI_Email { { if (function_exists('idn_to_ascii') && $atpos = strpos($email, '@')) { - $email = substr($email, 0, ++$atpos).idn_to_ascii(substr($email, $atpos)); + $email = self::substr($email, 0, ++$atpos).idn_to_ascii(self::substr($email, $atpos)); } return (bool) filter_var($email, FILTER_VALIDATE_EMAIL); @@ -1210,7 +1142,7 @@ class CI_Email { { // Is the line within the allowed character count? // If so we'll join it to the output and continue - if (mb_strlen($line) <= $charlim) + if (self::strlen($line) <= $charlim) { $output .= $line.$this->newline; continue; @@ -1226,10 +1158,10 @@ class CI_Email { } // Trim the word down - $temp .= mb_substr($line, 0, $charlim - 1); - $line = mb_substr($line, $charlim - 1); + $temp .= self::substr($line, 0, $charlim - 1); + $line = self::substr($line, $charlim - 1); } - while (mb_strlen($line) > $charlim); + while (self::strlen($line) > $charlim); // If $temp contains data it means we had to split up an over-length // word into smaller chunks so we'll add it back to our current line @@ -1258,10 +1190,11 @@ class CI_Email { /** * Build final headers * - * @return string + * @return void */ protected function _build_headers() { + $this->set_header('User-Agent', $this->useragent); $this->set_header('X-Sender', $this->clean_email($this->_headers['From'])); $this->set_header('X-Mailer', $this->useragent); $this->set_header('X-Priority', $this->_priorities[$this->priority]); @@ -1320,7 +1253,6 @@ class CI_Email { $this->_body = $this->word_wrap($this->_body); } - $this->_set_boundaries(); $this->_write_headers(); $hdr = ($this->_get_protocol() === 'mail') ? $this->newline : ''; @@ -1328,7 +1260,7 @@ class CI_Email { switch ($this->_get_content_type()) { - case 'plain' : + case 'plain': $hdr .= 'Content-Type: text/plain; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: '.$this->_get_encoding(); @@ -1345,7 +1277,7 @@ class CI_Email { return; - case 'html' : + case 'html': if ($this->send_multipart === FALSE) { @@ -1354,14 +1286,16 @@ class CI_Email { } else { - $hdr .= 'Content-Type: multipart/alternative; boundary="'.$this->_alt_boundary.'"'; + $boundary = uniqid('B_ALT_'); + $hdr .= 'Content-Type: multipart/alternative; boundary="'.$boundary.'"'; $body .= $this->_get_mime_message().$this->newline.$this->newline - .'--'.$this->_alt_boundary.$this->newline + .'--'.$boundary.$this->newline .'Content-Type: text/plain; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline - .$this->_get_alt_message().$this->newline.$this->newline.'--'.$this->_alt_boundary.$this->newline + .$this->_get_alt_message().$this->newline.$this->newline + .'--'.$boundary.$this->newline .'Content-Type: text/html; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline; @@ -1380,14 +1314,15 @@ class CI_Email { if ($this->send_multipart !== FALSE) { - $this->_finalbody .= '--'.$this->_alt_boundary.'--'; + $this->_finalbody .= '--'.$boundary.'--'; } return; - case 'plain-attach' : + case 'plain-attach': - $hdr .= 'Content-Type: multipart/'.$this->multipart.'; boundary="'.$this->_atc_boundary.'"'; + $boundary = uniqid('B_ATC_'); + $hdr .= 'Content-Type: multipart/mixed; boundary="'.$boundary.'"'; if ($this->_get_protocol() === 'mail') { @@ -1396,59 +1331,83 @@ class CI_Email { $body .= $this->_get_mime_message().$this->newline .$this->newline - .'--'.$this->_atc_boundary.$this->newline + .'--'.$boundary.$this->newline .'Content-Type: text/plain; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline .$this->newline .$this->_body.$this->newline.$this->newline; - break; - case 'html-attach' : + $this->_append_attachments($body, $boundary); + + break; + case 'html-attach': - $hdr .= 'Content-Type: multipart/'.$this->multipart.'; boundary="'.$this->_atc_boundary.'"'; + $alt_boundary = uniqid('B_ALT_'); + $last_boundary = NULL; + + if ($this->_attachments_have_multipart('mixed')) + { + $atc_boundary = uniqid('B_ATC_'); + $hdr .= 'Content-Type: multipart/mixed; boundary="'.$atc_boundary.'"'; + $last_boundary = $atc_boundary; + } + + if ($this->_attachments_have_multipart('related')) + { + $rel_boundary = uniqid('B_REL_'); + $rel_boundary_header = 'Content-Type: multipart/related; boundary="'.$rel_boundary.'"'; + + if (isset($last_boundary)) + { + $body .= '--'.$last_boundary.$this->newline.$rel_boundary_header; + } + else + { + $hdr .= $rel_boundary_header; + } + + $last_boundary = $rel_boundary; + } if ($this->_get_protocol() === 'mail') { $this->_header_str .= $hdr; } + self::strlen($body) && $body .= $this->newline.$this->newline; $body .= $this->_get_mime_message().$this->newline.$this->newline - .'--'.$this->_atc_boundary.$this->newline + .'--'.$last_boundary.$this->newline - .'Content-Type: multipart/alternative; boundary="'.$this->_alt_boundary.'"'.$this->newline.$this->newline - .'--'.$this->_alt_boundary.$this->newline + .'Content-Type: multipart/alternative; boundary="'.$alt_boundary.'"'.$this->newline.$this->newline + .'--'.$alt_boundary.$this->newline .'Content-Type: text/plain; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: '.$this->_get_encoding().$this->newline.$this->newline - .$this->_get_alt_message().$this->newline.$this->newline.'--'.$this->_alt_boundary.$this->newline + .$this->_get_alt_message().$this->newline.$this->newline + .'--'.$alt_boundary.$this->newline .'Content-Type: text/html; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline .$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline - .'--'.$this->_alt_boundary.'--'.$this->newline.$this->newline; - - break; - } + .'--'.$alt_boundary.'--'.$this->newline.$this->newline; - $attachment = array(); - for ($i = 0, $c = count($this->_attachments), $z = 0; $i < $c; $i++) - { - $filename = $this->_attachments[$i]['name'][0]; - $basename = ($this->_attachments[$i]['name'][1] === NULL) - ? basename($filename) : $this->_attachments[$i]['name'][1]; + if ( ! empty($rel_boundary)) + { + $body .= $this->newline.$this->newline; + $this->_append_attachments($body, $rel_boundary, 'related'); + } - $attachment[$z++] = '--'.$this->_atc_boundary.$this->newline - .'Content-type: '.$this->_attachments[$i]['type'].'; ' - .'name="'.$basename.'"'.$this->newline - .'Content-Disposition: '.$this->_attachments[$i]['disposition'].';'.$this->newline - .'Content-Transfer-Encoding: base64'.$this->newline - .(empty($this->_attachments[$i]['cid']) ? '' : 'Content-ID: <'.$this->_attachments[$i]['cid'].'>'.$this->newline); + // multipart/mixed attachments + if ( ! empty($atc_boundary)) + { + $body .= $this->newline.$this->newline; + $this->_append_attachments($body, $atc_boundary, 'mixed'); + } - $attachment[$z++] = $this->_attachments[$i]['content']; + break; } - $body .= implode($this->newline, $attachment).$this->newline.'--'.$this->_atc_boundary.'--'; $this->_finalbody = ($this->_get_protocol() === 'mail') ? $body : $hdr.$this->newline.$this->newline.$body; @@ -1458,6 +1417,58 @@ class CI_Email { // -------------------------------------------------------------------- + protected function _attachments_have_multipart($type) + { + foreach ($this->_attachments as &$attachment) + { + if ($attachment['multipart'] === $type) + { + return TRUE; + } + } + + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Prepares attachment string + * + * @param string $body Message body to append to + * @param string $boundary Multipart boundary + * @param string $multipart When provided, only attachments of this type will be processed + * @return string + */ + protected function _append_attachments(&$body, $boundary, $multipart = null) + { + for ($i = 0, $c = count($this->_attachments); $i < $c; $i++) + { + if (isset($multipart) && $this->_attachments[$i]['multipart'] !== $multipart) + { + continue; + } + + $name = isset($this->_attachments[$i]['name'][1]) + ? $this->_attachments[$i]['name'][1] + : basename($this->_attachments[$i]['name'][0]); + + $body .= '--'.$boundary.$this->newline + .'Content-Type: '.$this->_attachments[$i]['type'].'; name="'.$name.'"'.$this->newline + .'Content-Disposition: '.$this->_attachments[$i]['disposition'].';'.$this->newline + .'Content-Transfer-Encoding: base64'.$this->newline + .(empty($this->_attachments[$i]['cid']) ? '' : 'Content-ID: <'.$this->_attachments[$i]['cid'].'>'.$this->newline) + .$this->newline + .$this->_attachments[$i]['content'].$this->newline; + } + + // $name won't be set if no attachments were appended, + // and therefore a boundary wouldn't be necessary + empty($name) OR $body .= '--'.$boundary.'--'; + } + + // -------------------------------------------------------------------- + /** * Prep Quoted Printable * @@ -1469,6 +1480,20 @@ class CI_Email { */ protected function _prep_quoted_printable($str) { + // ASCII code numbers for "safe" characters that can always be + // used literally, without encoding, as described in RFC 2049. + // http://www.ietf.org/rfc/rfc2049.txt + static $ascii_safe_chars = array( + // ' ( ) + , - . / : = ? + 39, 40, 41, 43, 44, 45, 46, 47, 58, 61, 63, + // numbers + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + // upper-case letters + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + // lower-case letters + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122 + ); + // We are intentionally wrapping so mail servers will encode characters // properly and MUAs will behave, so {unwrap} must go! $str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str); @@ -1479,14 +1504,7 @@ class CI_Email { // which only works with "\n". if ($this->crlf === "\r\n") { - if (is_php('5.3')) - { - return quoted_printable_encode($str); - } - elseif (function_exists('imap_8bit')) - { - return imap_8bit($str); - } + return quoted_printable_encode($str); } // Reduce multiple spaces & remove nulls @@ -1503,7 +1521,7 @@ class CI_Email { foreach (explode("\n", $str) as $line) { - $length = strlen($line); + $length = self::strlen($line); $temp = ''; // Loop through each character in the line to add soft-wrap @@ -1516,18 +1534,29 @@ class CI_Email { $ascii = ord($char); // Convert spaces and tabs but only if it's the end of the line - if ($i === ($length - 1) && ($ascii === 32 OR $ascii === 9)) + if ($ascii === 32 OR $ascii === 9) { - $char = $escape.sprintf('%02s', dechex($ascii)); + if ($i === ($length - 1)) + { + $char = $escape.sprintf('%02s', dechex($ascii)); + } } - elseif ($ascii === 61) // encode = signs + // DO NOT move this below the $ascii_safe_chars line! + // + // = (equals) signs are allowed by RFC2049, but must be encoded + // as they are the encoding delimiter! + elseif ($ascii === 61) { $char = $escape.strtoupper(sprintf('%02s', dechex($ascii))); // =3D } + elseif ( ! in_array($ascii, $ascii_safe_chars, TRUE)) + { + $char = $escape.strtoupper(sprintf('%02s', dechex($ascii))); + } // If we're at the character limit, add the line to the output, // reset our temp variable, and keep on chuggin' - if ((strlen($temp) + strlen($char)) >= 76) + if ((self::strlen($temp) + self::strlen($char)) >= 76) { $output .= $temp.$escape.$this->crlf; $temp = ''; @@ -1542,7 +1571,7 @@ class CI_Email { } // get rid of extra CRLF tacked onto the end - return substr($output, 0, strlen($this->crlf) * -1); + return self::substr($output, 0, self::strlen($this->crlf) * -1); } // -------------------------------------------------------------------- @@ -1563,11 +1592,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( @@ -1585,18 +1613,22 @@ class CI_Email { // iconv_mime_encode() will always put a header field name. // We've passed it an empty one, but it still prepends our // encoded string with ': ', so we need to strip it. - return substr($output, 2); + return self::substr($output, 2); } $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 - isset($chars) OR $chars = strlen($str); + isset($chars) OR $chars = self::strlen($str); $output = '=?'.$this->charset.'?Q?'; - for ($i = 0, $length = strlen($output); $i < $chars; $i++) + for ($i = 0, $length = self::strlen($output); $i < $chars; $i++) { $chr = ($this->charset === 'UTF-8' && ICONV_ENABLED === TRUE) ? '='.implode('=', str_split(strtoupper(bin2hex(iconv_substr($str, $i, 1, $this->charset))), 2)) @@ -1604,11 +1636,11 @@ class CI_Email { // RFC 2045 sets a limit of 76 characters per line. // We'll append ?= to the end of each line though. - if ($length + ($l = strlen($chr)) > 74) + if ($length + ($l = self::strlen($chr)) > 74) { $output .= '?='.$this->crlf // EOL .' =?'.$this->charset.'?Q?'.$chr; // New line - $length = 6 + strlen($this->charset) + $l; // Reset the length for the new line + $length = 6 + self::strlen($this->charset) + $l; // Reset the length for the new line } else { @@ -1701,14 +1733,14 @@ class CI_Email { if ($i === $float) { - $chunk[] = substr($set, 1); + $chunk[] = self::substr($set, 1); $float += $this->bcc_batch_size; $set = ''; } if ($i === $c-1) { - $chunk[] = substr($set, 1); + $chunk[] = self::substr($set, 1); } } @@ -1791,6 +1823,33 @@ class CI_Email { // -------------------------------------------------------------------- /** + * Validate email for shell + * + * Applies stricter, shell-safe validation to email addresses. + * Introduced to prevent RCE via sendmail's -f option. + * + * @see https://github.com/bcit-ci/CodeIgniter/issues/4963 + * @see https://gist.github.com/Zenexer/40d02da5e07f151adeaeeaa11af9ab36 + * @license https://creativecommons.org/publicdomain/zero/1.0/ CC0 1.0, Public Domain + * + * Credits for the base concept go to Paul Buonopane <paul@namepros.com> + * + * @param string $email + * @return bool + */ + protected function _validate_email_for_shell(&$email) + { + if (function_exists('idn_to_ascii') && $atpos = strpos($email, '@')) + { + $email = self::substr($email, 0, ++$atpos).idn_to_ascii(self::substr($email, $atpos)); + } + + return (filter_var($email, FILTER_VALIDATE_EMAIL) === $email && preg_match('#\A[a-z0-9._+-]+@[a-z0-9.-]{1,253}\z#i', $email)); + } + + // -------------------------------------------------------------------- + + /** * Send using mail() * * @return bool @@ -1802,16 +1861,18 @@ class CI_Email { $this->_recipients = implode(', ', $this->_recipients); } - if ($this->_safe_mode === TRUE) + // _validate_email_for_shell() below accepts by reference, + // so this needs to be assigned to a variable + $from = $this->clean_email($this->_headers['Return-Path']); + + if ( ! $this->_validate_email_for_shell($from)) { return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str); } - else - { - // most documentation of sendmail using the "-f" flag lacks a space after it, however - // we've encountered servers that seem to require it to be in place. - return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, '-f '.$this->clean_email($this->_headers['Return-Path'])); - } + + // most documentation of sendmail using the "-f" flag lacks a space after it, however + // we've encountered servers that seem to require it to be in place. + return mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, '-f '.$from); } // -------------------------------------------------------------------- @@ -1823,14 +1884,22 @@ class CI_Email { */ protected function _send_with_sendmail() { + // _validate_email_for_shell() below accepts by reference, + // so this needs to be assigned to a variable + $from = $this->clean_email($this->_headers['From']); + if ($this->_validate_email_for_shell($from)) + { + $from = '-f '.$from; + } + else + { + $from = ''; + } + // is popen() enabled? - if ( ! function_usable('popen') - OR FALSE === ($fp = @popen( - $this->mailpath.' -oi -f '.$this->clean_email($this->_headers['From']) - .' -t -r '.$this->clean_email($this->_headers['Return-Path']) - , 'w')) - ) // server probably has popen disabled, so nothing we can do to get a verbose error. + if ( ! function_usable('popen') OR FALSE === ($fp = @popen($this->mailpath.' -oi '.$from.' -t', 'w'))) { + // server probably has popen disabled, so nothing we can do to get a verbose error. return FALSE; } @@ -1869,20 +1938,29 @@ 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']))) + { + $this->_smtp_end(); + return FALSE; + } foreach ($this->_recipients as $val) { - $this->_send_command('to', $val); + if ( ! $this->_send_command('to', $val)) + { + $this->_smtp_end(); + 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); + $this->_smtp_end(); + return FALSE; } } } @@ -1891,14 +1969,19 @@ class CI_Email { { foreach ($this->_bcc_array as $val) { - if ($val !== '') + if ($val !== '' && ! $this->_send_command('to', $val)) { - $this->_send_command('to', $val); + $this->_smtp_end(); + return FALSE; } } } - $this->_send_command('data'); + if ( ! $this->_send_command('data')) + { + $this->_smtp_end(); + 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)); @@ -1906,30 +1989,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 @@ -1982,7 +2073,7 @@ class CI_Email { * * @param string * @param string - * @return string + * @return bool */ protected function _send_command($cmd, $data = '') { @@ -2045,7 +2136,7 @@ class CI_Email { $this->_debug_msg[] = '<pre>'.$cmd.': '.$reply.'</pre>'; - if ((int) substr($reply, 0, 3) !== $resp) + if ((int) self::substr($reply, 0, 3) !== $resp) { $this->_set_error_message('lang:email_smtp_error', $reply); return FALSE; @@ -2113,6 +2204,11 @@ class CI_Email { return FALSE; } + if ($this->smtp_keepalive) + { + $this->_smtp_auth = FALSE; + } + return TRUE; } @@ -2127,9 +2223,9 @@ class CI_Email { protected function _send_data($data) { $data .= $this->newline; - for ($written = $timestamp = 0, $length = strlen($data); $written < $length; $written += $result) + for ($written = $timestamp = 0, $length = self::strlen($data); $written < $length; $written += $result) { - if (($result = fwrite($this->_smtp_connect, substr($data, $written))) === FALSE) + if (($result = fwrite($this->_smtp_connect, self::substr($data, $written))) === FALSE) { break; } @@ -2302,4 +2398,52 @@ class CI_Email { return 'application/x-unknown-content-type'; } + // -------------------------------------------------------------------- + + /** + * Destructor + * + * @return void + */ + public function __destruct() + { + is_resource($this->_smtp_connect) && $this->_send_command('quit'); + } + + // -------------------------------------------------------------------- + + /** + * Byte-safe strlen() + * + * @param string $str + * @return int + */ + protected static function strlen($str) + { + return (self::$func_override) + ? mb_strlen($str, '8bit') + : strlen($str); + } + + // -------------------------------------------------------------------- + + /** + * Byte-safe substr() + * + * @param string $str + * @param int $start + * @param int $length + * @return string + */ + protected static function substr($str, $start, $length = NULL) + { + if (self::$func_override) + { + return mb_substr($str, $start, $length, '8bit'); + } + + return isset($length) + ? substr($str, $start, $length) + : substr($str, $start); + } } diff --git a/system/libraries/Encrypt.php b/system/libraries/Encrypt.php index a46d4f462..46f374726 100644 --- a/system/libraries/Encrypt.php +++ b/system/libraries/Encrypt.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -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 https://codeigniter.com/user_guide/libraries/encryption.html */ class CI_Encrypt { @@ -198,7 +198,7 @@ class CI_Encrypt { * This allows for backwards compatibility and a method to transition to the * new encryption algorithms. * - * For more details, see http://codeigniter.com/user_guide/installation/upgrade_200.html#encryption + * For more details, see https://codeigniter.com/user_guide/installation/upgrade_200.html#encryption * * @param string * @param int (mcrypt mode constant) diff --git a/system/libraries/Encryption.php b/system/libraries/Encryption.php index f3e039881..6799d0fef 100644 --- a/system/libraries/Encryption.php +++ b/system/libraries/Encryption.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ @@ -46,7 +46,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Libraries * @author Andrey Andreev - * @link http://codeigniter.com/user_guide/libraries/encryption.html + * @link https://codeigniter.com/user_guide/libraries/encryption.html */ class CI_Encryption { @@ -152,10 +152,8 @@ class CI_Encryption { public function __construct(array $params = array()) { $this->_drivers = array( - 'mcrypt' => defined('MCRYPT_DEV_URANDOM'), - // While OpenSSL is available for PHP 5.3.0, an IV parameter - // for the encrypt/decrypt functions is only available since 5.3.3 - 'openssl' => (is_php('5.3.3') && extension_loaded('openssl')) + 'mcrypt' => defined('MCRYPT_DEV_URANDOM'), + 'openssl' => extension_loaded('openssl') ); if ( ! $this->_drivers['mcrypt'] && ! $this->_drivers['openssl']) @@ -337,9 +335,28 @@ class CI_Encryption { */ public function create_key($length) { - return ($this->_driver === 'mcrypt') - ? mcrypt_create_iv($length, MCRYPT_DEV_URANDOM) - : openssl_random_pseudo_bytes($length); + if (function_exists('random_bytes')) + { + 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); + } + + $is_secure = NULL; + $key = openssl_random_pseudo_bytes($length, $is_secure); + return ($is_secure === TRUE) + ? $key + : FALSE; } // -------------------------------------------------------------------- @@ -395,7 +412,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) @@ -458,14 +475,14 @@ 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( $data, $params['handle'], $params['key'], - 1, // DO NOT TOUCH! + OPENSSL_RAW_DATA, $iv ); @@ -624,7 +641,7 @@ class CI_Encryption { $data, $params['handle'], $params['key'], - 1, // DO NOT TOUCH! + OPENSSL_RAW_DATA, $iv ); } @@ -890,7 +907,7 @@ class CI_Encryption { * Byte-safe strlen() * * @param string $str - * @return integer + * @return int */ protected static function strlen($str) { @@ -913,9 +930,6 @@ class CI_Encryption { { if (self::$func_override) { - // mb_substr($str, $start, null, '8bit') returns an empty - // string on PHP 5.3 - isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start); return mb_substr($str, $start, $length, '8bit'); } diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php index d9ecc45f9..7be0b949d 100644 --- a/system/libraries/Form_validation.php +++ b/system/libraries/Form_validation.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Validation * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/form_validation.html + * @link https://codeigniter.com/user_guide/libraries/form_validation.html */ class CI_Form_validation { @@ -164,7 +164,7 @@ class CI_Form_validation { * @param array $errors * @return CI_Form_validation */ - public function set_rules($field, $label = '', $rules = array(), $errors = array()) + public function set_rules($field, $label = null, $rules = null, $errors = array()) { // No reason to set rules if we have no POST data // or a validation array has not been specified @@ -197,6 +197,10 @@ class CI_Form_validation { return $this; } + elseif ( ! isset($rules)) + { + throw new BadMethodCallException('Form_validation: set_rules() called without a $rules parameter'); + } // No fields or no rules? Nothing to do... if ( ! is_string($field) OR $field === '' OR empty($rules)) @@ -211,7 +215,7 @@ class CI_Form_validation { return $this; } - $rules = explode('|', $rules); + $rules = preg_split('/\|(?![^\[]*\])/', $rules); } // If the field label wasn't passed we use the field name @@ -415,12 +419,9 @@ class CI_Form_validation { */ public function run($group = '') { - // Do we even have any data to process? Mm? - $validation_array = empty($this->validation_data) ? $_POST : $this->validation_data; - if (count($validation_array) === 0) - { - return FALSE; - } + $validation_array = empty($this->validation_data) + ? $_POST + : $this->validation_data; // Does the _field_data array containing the validation rules exist? // If not, we look to see if they were assigned via a config file @@ -453,7 +454,7 @@ class CI_Form_validation { $this->CI->lang->load('form_validation'); // Cycle through the rules for each field and match the corresponding $validation_data item - foreach ($this->_field_data as $field => $row) + foreach ($this->_field_data as $field => &$row) { // Fetch the data from the validation_data array item and cache it in the _field_data array. // Depending on whether the field name is an array or a string will determine where we get it from. @@ -470,7 +471,7 @@ class CI_Form_validation { // Execute validation rules // Note: A second foreach (for now) is required in order to avoid false-positives // for rules like 'matches', which correlate to other validation fields. - foreach ($this->_field_data as $field => $row) + foreach ($this->_field_data as $field => &$row) { // Don't try to validate if we have no rules set if (empty($row['rules'])) @@ -478,7 +479,7 @@ class CI_Form_validation { continue; } - $this->_execute($row, $row['rules'], $this->_field_data[$field]['postdata']); + $this->_execute($row, $row['rules'], $row['postdata']); } // Did we end up with any errors? @@ -489,7 +490,7 @@ class CI_Form_validation { } // Now we need to re-set the POST data with the new, processed data - $this->_reset_post_array(); + empty($this->validation_data) && $this->_reset_post_array(); return ($total_errors === 0); } @@ -497,6 +498,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 @@ -530,10 +588,7 @@ class CI_Form_validation { { if ($row['is_array'] === FALSE) { - if (isset($_POST[$row['field']])) - { - $_POST[$row['field']] = $row['postdata']; - } + isset($_POST[$field]) && $_POST[$field] = $row['postdata']; } else { @@ -553,20 +608,7 @@ class CI_Form_validation { } } - if (is_array($row['postdata'])) - { - $array = array(); - foreach ($row['postdata'] as $k => $v) - { - $array[$k] = $v; - } - - $post_ref = $array; - } - else - { - $post_ref = $row['postdata']; - } + $post_ref = $row['postdata']; } } } @@ -586,7 +628,10 @@ class CI_Form_validation { protected function _execute($row, $rules, $postdata = NULL, $cycles = 0) { // If the $_POST data is an array we will run a recursive call - if (is_array($postdata)) + // + // Note: We MUST check if the array is empty or not! + // Otherwise empty arrays will always pass validation. + if (is_array($postdata) && ! empty($postdata)) { foreach ($postdata as $key => $val) { @@ -596,84 +641,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'; - - // Check if a custom message is defined - if (isset($this->_field_data[$row['field']]['errors'][$type])) - { - $line = $this->_field_data[$row['field']]['errors'][$type]; - } - elseif (isset($this->_error_messages[$type])) - { - $line = $this->_error_messages[$type]; - } - elseif (FALSE === ($line = $this->CI->lang->line('form_validation_'.$type)) - // DEPRECATED support for non-prefixed keys - && FALSE === ($line = $this->CI->lang->line($type, FALSE))) - { - $line = 'The field was not set'; - } - - // 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; @@ -732,6 +700,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) { @@ -770,12 +749,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)) { @@ -823,23 +796,9 @@ class CI_Form_validation { { $line = $this->CI->lang->line('form_validation_error_message_not_set').'(Anonymous function)'; } - // Check if a custom message is defined - elseif (isset($this->_field_data[$row['field']]['errors'][$rule])) - { - $line = $this->_field_data[$row['field']]['errors'][$rule]; - } - elseif ( ! isset($this->_error_messages[$rule])) - { - if (FALSE === ($line = $this->CI->lang->line('form_validation_'.$rule)) - // 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').'('.$rule.')'; - } - } else { - $line = $this->_error_messages[$rule]; + $line = $this->_get_error_message($rule, $row['field']); } // Is the parameter we are inserting into the error message the name @@ -868,6 +827,40 @@ class CI_Form_validation { // -------------------------------------------------------------------- /** + * Get the error message for the rule + * + * @param string $rule The rule name + * @param string $field The field name + * @return string + */ + protected function _get_error_message($rule, $field) + { + // check if a custom message is defined through validation config row. + if (isset($this->_field_data[$field]['errors'][$rule])) + { + return $this->_field_data[$field]['errors'][$rule]; + } + // check if a custom message has been set using the set_message() function + elseif (isset($this->_error_messages[$rule])) + { + return $this->_error_messages[$rule]; + } + elseif (FALSE !== ($line = $this->CI->lang->line('form_validation_'.$rule))) + { + return $line; + } + // DEPRECATED support for non-prefixed keys, lang file again + elseif (FALSE !== ($line = $this->CI->lang->line($rule, FALSE))) + { + return $line; + } + + return $this->CI->lang->line('form_validation_error_message_not_set').'('.$rule.')'; + } + + // -------------------------------------------------------------------- + + /** * Translate a field name * * @param string the field name @@ -1065,7 +1058,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) !== ''); } // -------------------------------------------------------------------- @@ -1209,7 +1204,7 @@ class CI_Form_validation { { return FALSE; } - elseif ( ! in_array($matches[1], array('http', 'https'), TRUE)) + elseif ( ! in_array(strtolower($matches[1]), array('http', 'https'), TRUE)) { return FALSE; } @@ -1217,18 +1212,15 @@ class CI_Form_validation { $str = $matches[2]; } - $str = 'http://'.$str; - - // There's a bug affecting PHP 5.2.13, 5.3.2 that considers the - // underscore to be a valid hostname character instead of a dash. - // Reference: https://bugs.php.net/bug.php?id=51192 - if (version_compare(PHP_VERSION, '5.2.13', '==') OR version_compare(PHP_VERSION, '5.3.2', '==')) + // PHP 7 accepts IPv6 addresses within square brackets as hostnames, + // but it appears that the PR that came in with https://bugs.php.net/bug.php?id=68039 + // was never merged into a PHP 5 branch ... https://3v4l.org/8PsSN + if (preg_match('/^\[([^\]]+)\]/', $str, $matches) && ! is_php('7') && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== FALSE) { - sscanf($str, 'http://%[^/]', $host); - $str = substr_replace($str, strtr($host, array('_' => '-', '-' => '_')), 7, strlen($host)); + $str = 'ipv6.host'.substr($str, strlen($matches[1]) + 2); } - return (filter_var($str, FILTER_VALIDATE_URL) !== FALSE); + return (filter_var('http://'.$str, FILTER_VALIDATE_URL) !== FALSE); } // -------------------------------------------------------------------- @@ -1241,9 +1233,9 @@ class CI_Form_validation { */ public function valid_email($str) { - if (function_exists('idn_to_ascii') && $atpos = strpos($str, '@')) + if (function_exists('idn_to_ascii') && sscanf($str, '%[^@]@%s', $name, $domain) === 2) { - $str = substr($str, 0, ++$atpos).idn_to_ascii(substr($str, $atpos)); + $str = $name.'@'.idn_to_ascii($domain); } return (bool) filter_var($str, FILTER_VALIDATE_EMAIL); @@ -1292,6 +1284,31 @@ class CI_Form_validation { // -------------------------------------------------------------------- /** + * Validate MAC address + * + * @param string $mac + * @return bool + */ + public function valid_mac($mac) + { + if ( ! is_php('5.5')) + { + // Most common format, with either dash or colon delimiters + if (preg_match('#\A[0-9a-f]{2}(?<delimiter>[:-])([0-9a-f]{2}(?P=delimiter)){4}[0-9a-f]{2}\z#i', $mac)) + { + return TRUE; + } + + // The less common format; e.g. 0123.4567.89ab + return (bool) preg_match('#((\A|\.)[0-9a-f]{4}){3}\z#i', $mac); + } + + return (bool) filter_var($mac, FILTER_VALIDATE_MAC); + } + + // -------------------------------------------------------------------- + + /** * Alpha * * @param string @@ -1496,37 +1513,6 @@ class CI_Form_validation { // -------------------------------------------------------------------- /** - * Prep data for form - * - * This function allows HTML to be safely shown in a form. - * Special characters are converted. - * - * @param string - * @return string - */ - public function prep_for_form($data = '') - { - if ($this->_safe_form_data === FALSE OR empty($data)) - { - return $data; - } - - if (is_array($data)) - { - foreach ($data as $key => $val) - { - $data[$key] = $this->prep_for_form($val); - } - - return $data; - } - - return str_replace(array("'", '"', '<', '>'), array(''', '"', '<', '>'), stripslashes($data)); - } - - // -------------------------------------------------------------------- - - /** * Prep URL * * @param string @@ -1534,12 +1520,7 @@ class CI_Form_validation { */ public function prep_url($str = '') { - if ($str === 'http://' OR $str === '') - { - return ''; - } - - if (strpos($str, 'http://') !== 0 && strpos($str, 'https://') !== 0) + if ($str !== '' && stripos($str, 'http://') !== 0 && stripos($str, 'https://') !== 0) { return 'http://'.$str; } @@ -1586,7 +1567,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 2d345c294..86e5b8f33 100644 --- a/system/libraries/Ftp.php +++ b/system/libraries/Ftp.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -44,7 +44,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/ftp.html + * @link https://codeigniter.com/user_guide/libraries/ftp.html */ class CI_FTP { @@ -486,7 +486,7 @@ class CI_FTP { { for ($i = 0, $c = count($list); $i < $c; $i++) { - // If we can't delete the item it's probaly a directory, + // If we can't delete the item it's probably a directory, // so we'll recursively call delete_dir() if ( ! preg_match('#/\.\.?$#', $list[$i]) && ! @ftp_delete($this->conn_id, $list[$i])) { diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php index e813efd89..8b5a1adb0 100644 --- a/system/libraries/Image_lib.php +++ b/system/libraries/Image_lib.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Image_lib * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/image_lib.html + * @link https://codeigniter.com/user_guide/libraries/image_lib.html */ class CI_Image_lib { @@ -392,6 +392,16 @@ class CI_Image_lib { $this->initialize($props); } + /** + * A work-around for some improperly formatted, but + * usable JPEGs; known to be produced by Samsung + * smartphones' front-facing cameras. + * + * @see https://github.com/bcit-ci/CodeIgniter/issues/4967 + * @see https://bugs.php.net/bug.php?id=72404 + */ + ini_set('gd.jpeg_ignore_warning', 1); + log_message('info', 'Image Lib Class Initialized'); } @@ -456,7 +466,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 +488,10 @@ class CI_Image_lib { continue; } } + elseif (in_array($key, array('width', 'height'), TRUE) && ! ctype_digit((string) $val)) + { + continue; + } $this->$key = $val; } @@ -540,37 +554,30 @@ class CI_Image_lib { */ if ($this->new_image === '') { - $this->dest_image = $this->source_image; + $this->dest_image = $this->source_image; $this->dest_folder = $this->source_folder; } - elseif (strpos($this->new_image, '/') === FALSE) + elseif (strpos($this->new_image, '/') === FALSE && strpos($this->new_image, '\\') === FALSE) { + $this->dest_image = $this->new_image; $this->dest_folder = $this->source_folder; - $this->dest_image = $this->new_image; } else { - if (strpos($this->new_image, '/') === FALSE && strpos($this->new_image, '\\') === FALSE) - { - $full_dest_path = str_replace('\\', '/', realpath($this->new_image)); - } - else - { - $full_dest_path = $this->new_image; - } - // Is there a file name? - if ( ! preg_match('#\.(jpg|jpeg|gif|png)$#i', $full_dest_path)) + if ( ! preg_match('#\.(jpg|jpeg|gif|png)$#i', $this->new_image)) { - $this->dest_folder = $full_dest_path.'/'; - $this->dest_image = $this->source_image; + $this->dest_image = $this->source_image; + $this->dest_folder = $this->new_image; } else { - $x = explode('/', $full_dest_path); - $this->dest_image = end($x); - $this->dest_folder = str_replace($this->dest_image, '', $full_dest_path); + $x = explode('/', str_replace('\\', '/', $this->new_image)); + $this->dest_image = end($x); + $this->dest_folder = str_replace($this->dest_image, '', $this->new_image); } + + $this->dest_folder = realpath($this->dest_folder).'/'; } /* Compile the finalized filenames/paths @@ -862,27 +869,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')) @@ -1196,7 +1204,7 @@ class CI_Image_lib { } // Build the finalized image - if ($wm_img_type === 3 && function_exists('imagealphablending')) + if ($wm_img_type === 3) { @imagealphablending($src_img, TRUE); } @@ -1641,25 +1649,31 @@ class CI_Image_lib { } $vals = getimagesize($path); + if ($vals === FALSE) + { + $this->set_error('imglib_invalid_image'); + return FALSE; + } + $types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png'); - $mime = (isset($types[$vals[2]])) ? 'image/'.$types[$vals[2]] : 'image/jpg'; + $mime = isset($types[$vals[2]]) ? 'image/'.$types[$vals[2]] : 'image/jpg'; if ($return === TRUE) { return array( - 'width' => $vals[0], - 'height' => $vals[1], - 'image_type' => $vals[2], - 'size_str' => $vals[3], - 'mime_type' => $mime - ); - } - - $this->orig_width = $vals[0]; - $this->orig_height = $vals[1]; - $this->image_type = $vals[2]; - $this->size_str = $vals[3]; - $this->mime_type = $mime; + 'width' => $vals[0], + 'height' => $vals[1], + 'image_type' => $vals[2], + 'size_str' => $vals[3], + 'mime_type' => $mime + ); + } + + $this->orig_width = $vals[0]; + $this->orig_height = $vals[1]; + $this->image_type = $vals[2]; + $this->size_str = $vals[3]; + $this->mime_type = $mime; return TRUE; } diff --git a/system/libraries/Javascript.php b/system/libraries/Javascript.php deleted file mode 100644 index 4cc628692..000000000 --- a/system/libraries/Javascript.php +++ /dev/null @@ -1,856 +0,0 @@ -<?php -/** - * CodeIgniter - * - * An open source application development framework for PHP - * - * This content is released under the MIT License (MIT) - * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @package CodeIgniter - * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) - * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com - * @since Version 1.0.0 - * @filesource - */ -defined('BASEPATH') OR exit('No direct script access allowed'); - -/** - * Javascript Class - * - * @package CodeIgniter - * @subpackage Libraries - * @category Javascript - * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/javascript.html - * @deprecated 3.0.0 This was never a good idea in the first place. - */ -class CI_Javascript { - - /** - * JavaScript location - * - * @var string - */ - protected $_javascript_location = 'js'; - - // -------------------------------------------------------------------- - - /** - * Constructor - * - * @param array $params - * @return void - */ - public function __construct($params = array()) - { - $defaults = array('js_library_driver' => 'jquery', 'autoload' => TRUE); - - foreach ($defaults as $key => $val) - { - if (isset($params[$key]) && $params[$key] !== '') - { - $defaults[$key] = $params[$key]; - } - } - - extract($defaults); - - $this->CI =& get_instance(); - - // load the requested js library - $this->CI->load->library('Javascript/'.$js_library_driver, array('autoload' => $autoload)); - // make js to refer to current library - $this->js =& $this->CI->$js_library_driver; - - log_message('info', 'Javascript Class Initialized and loaded. Driver used: '.$js_library_driver); - } - - // -------------------------------------------------------------------- - // Event Code - // -------------------------------------------------------------------- - - /** - * Blur - * - * Outputs a javascript library blur event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function blur($element = 'this', $js = '') - { - return $this->js->_blur($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Change - * - * Outputs a javascript library change event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function change($element = 'this', $js = '') - { - return $this->js->_change($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Click - * - * Outputs a javascript library click event - * - * @param string The element to attach the event to - * @param string The code to execute - * @param bool whether or not to return false - * @return string - */ - public function click($element = 'this', $js = '', $ret_false = TRUE) - { - return $this->js->_click($element, $js, $ret_false); - } - - // -------------------------------------------------------------------- - - /** - * Double Click - * - * Outputs a javascript library dblclick event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function dblclick($element = 'this', $js = '') - { - return $this->js->_dblclick($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Error - * - * Outputs a javascript library error event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function error($element = 'this', $js = '') - { - return $this->js->_error($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Focus - * - * Outputs a javascript library focus event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function focus($element = 'this', $js = '') - { - return $this->js->_focus($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Hover - * - * Outputs a javascript library hover event - * - * @param string - element - * @param string - Javascript code for mouse over - * @param string - Javascript code for mouse out - * @return string - */ - public function hover($element = 'this', $over = '', $out = '') - { - return $this->js->_hover($element, $over, $out); - } - - // -------------------------------------------------------------------- - - /** - * Keydown - * - * Outputs a javascript library keydown event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function keydown($element = 'this', $js = '') - { - return $this->js->_keydown($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Keyup - * - * Outputs a javascript library keydown event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function keyup($element = 'this', $js = '') - { - return $this->js->_keyup($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Load - * - * Outputs a javascript library load event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function load($element = 'this', $js = '') - { - return $this->js->_load($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Mousedown - * - * Outputs a javascript library mousedown event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function mousedown($element = 'this', $js = '') - { - return $this->js->_mousedown($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Mouse Out - * - * Outputs a javascript library mouseout event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function mouseout($element = 'this', $js = '') - { - return $this->js->_mouseout($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Mouse Over - * - * Outputs a javascript library mouseover event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function mouseover($element = 'this', $js = '') - { - return $this->js->_mouseover($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Mouseup - * - * Outputs a javascript library mouseup event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function mouseup($element = 'this', $js = '') - { - return $this->js->_mouseup($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Output - * - * Outputs the called javascript to the screen - * - * @param string The code to output - * @return string - */ - public function output($js) - { - return $this->js->_output($js); - } - - // -------------------------------------------------------------------- - - /** - * Ready - * - * Outputs a javascript library mouseup event - * - * @param string $js Code to execute - * @return string - */ - public function ready($js) - { - return $this->js->_document_ready($js); - } - - // -------------------------------------------------------------------- - - /** - * Resize - * - * Outputs a javascript library resize event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function resize($element = 'this', $js = '') - { - return $this->js->_resize($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Scroll - * - * Outputs a javascript library scroll event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function scroll($element = 'this', $js = '') - { - return $this->js->_scroll($element, $js); - } - - // -------------------------------------------------------------------- - - /** - * Unload - * - * Outputs a javascript library unload event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - public function unload($element = 'this', $js = '') - { - return $this->js->_unload($element, $js); - } - - // -------------------------------------------------------------------- - // Effects - // -------------------------------------------------------------------- - - /** - * Add Class - * - * Outputs a javascript library addClass event - * - * @param string - element - * @param string - Class to add - * @return string - */ - public function addClass($element = 'this', $class = '') - { - return $this->js->_addClass($element, $class); - } - - // -------------------------------------------------------------------- - - /** - * Animate - * - * Outputs a javascript library animate event - * - * @param string $element = 'this' - * @param array $params = array() - * @param mixed $speed 'slow', 'normal', 'fast', or time in milliseconds - * @param string $extra - * @return string - */ - public function animate($element = 'this', $params = array(), $speed = '', $extra = '') - { - return $this->js->_animate($element, $params, $speed, $extra); - } - - // -------------------------------------------------------------------- - - /** - * Fade In - * - * Outputs a javascript library hide event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - public function fadeIn($element = 'this', $speed = '', $callback = '') - { - return $this->js->_fadeIn($element, $speed, $callback); - } - - // -------------------------------------------------------------------- - - /** - * Fade Out - * - * Outputs a javascript library hide event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - public function fadeOut($element = 'this', $speed = '', $callback = '') - { - return $this->js->_fadeOut($element, $speed, $callback); - } - // -------------------------------------------------------------------- - - /** - * Slide Up - * - * Outputs a javascript library slideUp event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - public function slideUp($element = 'this', $speed = '', $callback = '') - { - return $this->js->_slideUp($element, $speed, $callback); - - } - - // -------------------------------------------------------------------- - - /** - * Remove Class - * - * Outputs a javascript library removeClass event - * - * @param string - element - * @param string - Class to add - * @return string - */ - public function removeClass($element = 'this', $class = '') - { - return $this->js->_removeClass($element, $class); - } - - // -------------------------------------------------------------------- - - /** - * Slide Down - * - * Outputs a javascript library slideDown event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - public function slideDown($element = 'this', $speed = '', $callback = '') - { - return $this->js->_slideDown($element, $speed, $callback); - } - - // -------------------------------------------------------------------- - - /** - * Slide Toggle - * - * Outputs a javascript library slideToggle event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - public function slideToggle($element = 'this', $speed = '', $callback = '') - { - return $this->js->_slideToggle($element, $speed, $callback); - - } - - // -------------------------------------------------------------------- - - /** - * Hide - * - * Outputs a javascript library hide action - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - public function hide($element = 'this', $speed = '', $callback = '') - { - return $this->js->_hide($element, $speed, $callback); - } - - // -------------------------------------------------------------------- - - /** - * Toggle - * - * Outputs a javascript library toggle event - * - * @param string - element - * @return string - */ - public function toggle($element = 'this') - { - return $this->js->_toggle($element); - - } - - // -------------------------------------------------------------------- - - /** - * Toggle Class - * - * Outputs a javascript library toggle class event - * - * @param string $element = 'this' - * @param string $class = '' - * @return string - */ - public function toggleClass($element = 'this', $class = '') - { - return $this->js->_toggleClass($element, $class); - } - - // -------------------------------------------------------------------- - - /** - * Show - * - * Outputs a javascript library show event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - public function show($element = 'this', $speed = '', $callback = '') - { - return $this->js->_show($element, $speed, $callback); - } - - // -------------------------------------------------------------------- - - /** - * Compile - * - * gather together all script needing to be output - * - * @param string $view_var - * @param bool $script_tags - * @return string - */ - public function compile($view_var = 'script_foot', $script_tags = TRUE) - { - $this->js->_compile($view_var, $script_tags); - } - - // -------------------------------------------------------------------- - - /** - * Clear Compile - * - * Clears any previous javascript collected for output - * - * @return void - */ - public function clear_compile() - { - $this->js->_clear_compile(); - } - - // -------------------------------------------------------------------- - - /** - * External - * - * Outputs a <script> tag with the source as an external js file - * - * @param string $external_file - * @param bool $relative - * @return string - */ - public function external($external_file = '', $relative = FALSE) - { - if ($external_file !== '') - { - $this->_javascript_location = $external_file; - } - elseif ($this->CI->config->item('javascript_location') !== '') - { - $this->_javascript_location = $this->CI->config->item('javascript_location'); - } - - if ($relative === TRUE OR strpos($external_file, 'http://') === 0 OR strpos($external_file, 'https://') === 0) - { - $str = $this->_open_script($external_file); - } - elseif (strpos($this->_javascript_location, 'http://') !== FALSE) - { - $str = $this->_open_script($this->_javascript_location.$external_file); - } - else - { - $str = $this->_open_script($this->CI->config->slash_item('base_url').$this->_javascript_location.$external_file); - } - - return $str.$this->_close_script(); - } - - // -------------------------------------------------------------------- - - /** - * Inline - * - * Outputs a <script> tag - * - * @param string The element to attach the event to - * @param bool If a CDATA section should be added - * @return string - */ - public function inline($script, $cdata = TRUE) - { - return $this->_open_script() - . ($cdata ? "\n// <![CDATA[\n".$script."\n// ]]>\n" : "\n".$script."\n") - . $this->_close_script(); - } - - // -------------------------------------------------------------------- - - /** - * Open Script - * - * Outputs an opening <script> - * - * @param string - * @return string - */ - protected function _open_script($src = '') - { - return '<script type="text/javascript" charset="'.strtolower($this->CI->config->item('charset')).'"' - .($src === '' ? '>' : ' src="'.$src.'">'); - } - - // -------------------------------------------------------------------- - - /** - * Close Script - * - * Outputs an closing </script> - * - * @param string - * @return string - */ - protected function _close_script($extra = "\n") - { - return '</script>'.$extra; - } - - // -------------------------------------------------------------------- - // AJAX-Y STUFF - still a testbed - // -------------------------------------------------------------------- - - /** - * Update - * - * Outputs a javascript library slideDown event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - public function update($element = 'this', $speed = '', $callback = '') - { - return $this->js->_updater($element, $speed, $callback); - } - - // -------------------------------------------------------------------- - - /** - * Generate JSON - * - * Can be passed a database result or associative array and returns a JSON formatted string - * - * @param mixed result set or array - * @param bool match array types (defaults to objects) - * @return string a json formatted string - */ - public function generate_json($result = NULL, $match_array_type = FALSE) - { - // JSON data can optionally be passed to this function - // either as a database result object or an array, or a user supplied array - if ($result !== NULL) - { - if (is_object($result)) - { - $json_result = is_callable(array($result, 'result_array')) ? $result->result_array() : (array) $result; - } - elseif (is_array($result)) - { - $json_result = $result; - } - else - { - return $this->_prep_args($result); - } - } - else - { - return 'null'; - } - - $json = array(); - $_is_assoc = TRUE; - - if ( ! is_array($json_result) && empty($json_result)) - { - show_error('Generate JSON Failed - Illegal key, value pair.'); - } - elseif ($match_array_type) - { - $_is_assoc = $this->_is_associative_array($json_result); - } - - foreach ($json_result as $k => $v) - { - if ($_is_assoc) - { - $json[] = $this->_prep_args($k, TRUE).':'.$this->generate_json($v, $match_array_type); - } - else - { - $json[] = $this->generate_json($v, $match_array_type); - } - } - - $json = implode(',', $json); - - return $_is_assoc ? '{'.$json.'}' : '['.$json.']'; - - } - - // -------------------------------------------------------------------- - - /** - * Is associative array - * - * Checks for an associative array - * - * @param array - * @return bool - */ - protected function _is_associative_array($arr) - { - foreach (array_keys($arr) as $key => $val) - { - if ($key !== $val) - { - return TRUE; - } - } - - return FALSE; - } - - // -------------------------------------------------------------------- - - /** - * Prep Args - * - * Ensures a standard json value and escapes values - * - * @param mixed $result - * @param bool $is_key = FALSE - * @return string - */ - protected function _prep_args($result, $is_key = FALSE) - { - if ($result === NULL) - { - return 'null'; - } - elseif (is_bool($result)) - { - return ($result === TRUE) ? 'true' : 'false'; - } - elseif (is_string($result) OR $is_key) - { - return '"'.str_replace(array('\\', "\t", "\n", "\r", '"', '/'), array('\\\\', '\\t', '\\n', "\\r", '\"', '\/'), $result).'"'; - } - elseif (is_scalar($result)) - { - return $result; - } - } - -} diff --git a/system/libraries/Javascript/Jquery.php b/system/libraries/Javascript/Jquery.php deleted file mode 100644 index 11f2d2361..000000000 --- a/system/libraries/Javascript/Jquery.php +++ /dev/null @@ -1,1076 +0,0 @@ -<?php -/** - * CodeIgniter - * - * An open source application development framework for PHP - * - * This content is released under the MIT License (MIT) - * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @package CodeIgniter - * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) - * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com - * @since Version 1.0.0 - * @filesource - */ -defined('BASEPATH') OR exit('No direct script access allowed'); - -/** - * Jquery Class - * - * @package CodeIgniter - * @subpackage Libraries - * @category Loader - * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/javascript.html - */ -class CI_Jquery extends CI_Javascript { - - /** - * JavaScript directory location - * - * @var string - */ - protected $_javascript_folder = 'js'; - - /** - * JQuery code for load - * - * @var array - */ - public $jquery_code_for_load = array(); - - /** - * JQuery code for compile - * - * @var array - */ - public $jquery_code_for_compile = array(); - - /** - * JQuery corner active flag - * - * @var bool - */ - public $jquery_corner_active = FALSE; - - /** - * JQuery table sorter active flag - * - * @var bool - */ - public $jquery_table_sorter_active = FALSE; - - /** - * JQuery table sorter pager active - * - * @var bool - */ - public $jquery_table_sorter_pager_active = FALSE; - - /** - * JQuery AJAX image - * - * @var string - */ - public $jquery_ajax_img = ''; - - // -------------------------------------------------------------------- - - /** - * Constructor - * - * @param array $params - * @return void - */ - public function __construct($params) - { - $this->CI =& get_instance(); - extract($params); - - if ($autoload === TRUE) - { - $this->script(); - } - - log_message('info', 'Jquery Class Initialized'); - } - - // -------------------------------------------------------------------- - // Event Code - // -------------------------------------------------------------------- - - /** - * Blur - * - * Outputs a jQuery blur event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _blur($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'blur'); - } - - // -------------------------------------------------------------------- - - /** - * Change - * - * Outputs a jQuery change event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _change($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'change'); - } - - // -------------------------------------------------------------------- - - /** - * Click - * - * Outputs a jQuery click event - * - * @param string The element to attach the event to - * @param string The code to execute - * @param bool whether or not to return false - * @return string - */ - protected function _click($element = 'this', $js = '', $ret_false = TRUE) - { - is_array($js) OR $js = array($js); - - if ($ret_false) - { - $js[] = 'return false;'; - } - - return $this->_add_event($element, $js, 'click'); - } - - // -------------------------------------------------------------------- - - /** - * Double Click - * - * Outputs a jQuery dblclick event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _dblclick($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'dblclick'); - } - - // -------------------------------------------------------------------- - - /** - * Error - * - * Outputs a jQuery error event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _error($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'error'); - } - - // -------------------------------------------------------------------- - - /** - * Focus - * - * Outputs a jQuery focus event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _focus($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'focus'); - } - - // -------------------------------------------------------------------- - - /** - * Hover - * - * Outputs a jQuery hover event - * - * @param string - element - * @param string - Javascript code for mouse over - * @param string - Javascript code for mouse out - * @return string - */ - protected function _hover($element = 'this', $over = '', $out = '') - { - $event = "\n\t$(".$this->_prep_element($element).").hover(\n\t\tfunction()\n\t\t{\n\t\t\t{$over}\n\t\t}, \n\t\tfunction()\n\t\t{\n\t\t\t{$out}\n\t\t});\n"; - - $this->jquery_code_for_compile[] = $event; - - return $event; - } - - // -------------------------------------------------------------------- - - /** - * Keydown - * - * Outputs a jQuery keydown event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _keydown($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'keydown'); - } - - // -------------------------------------------------------------------- - - /** - * Keyup - * - * Outputs a jQuery keydown event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _keyup($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'keyup'); - } - - // -------------------------------------------------------------------- - - /** - * Load - * - * Outputs a jQuery load event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _load($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'load'); - } - - // -------------------------------------------------------------------- - - /** - * Mousedown - * - * Outputs a jQuery mousedown event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _mousedown($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'mousedown'); - } - - // -------------------------------------------------------------------- - - /** - * Mouse Out - * - * Outputs a jQuery mouseout event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _mouseout($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'mouseout'); - } - - // -------------------------------------------------------------------- - - /** - * Mouse Over - * - * Outputs a jQuery mouseover event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _mouseover($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'mouseover'); - } - - // -------------------------------------------------------------------- - - /** - * Mouseup - * - * Outputs a jQuery mouseup event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _mouseup($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'mouseup'); - } - - // -------------------------------------------------------------------- - - /** - * Output - * - * Outputs script directly - * - * @param array $array_js = array() - * @return void - */ - protected function _output($array_js = array()) - { - if ( ! is_array($array_js)) - { - $array_js = array($array_js); - } - - foreach ($array_js as $js) - { - $this->jquery_code_for_compile[] = "\t".$js."\n"; - } - } - - // -------------------------------------------------------------------- - - /** - * Resize - * - * Outputs a jQuery resize event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _resize($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'resize'); - } - - // -------------------------------------------------------------------- - - /** - * Scroll - * - * Outputs a jQuery scroll event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _scroll($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'scroll'); - } - - // -------------------------------------------------------------------- - - /** - * Unload - * - * Outputs a jQuery unload event - * - * @param string The element to attach the event to - * @param string The code to execute - * @return string - */ - protected function _unload($element = 'this', $js = '') - { - return $this->_add_event($element, $js, 'unload'); - } - - // -------------------------------------------------------------------- - // Effects - // -------------------------------------------------------------------- - - /** - * Add Class - * - * Outputs a jQuery addClass event - * - * @param string $element - * @param string $class - * @return string - */ - protected function _addClass($element = 'this', $class = '') - { - $element = $this->_prep_element($element); - return '$('.$element.').addClass("'.$class.'");'; - } - - // -------------------------------------------------------------------- - - /** - * Animate - * - * Outputs a jQuery animate event - * - * @param string $element - * @param array $params - * @param string $speed 'slow', 'normal', 'fast', or time in milliseconds - * @param string $extra - * @return string - */ - protected function _animate($element = 'this', $params = array(), $speed = '', $extra = '') - { - $element = $this->_prep_element($element); - $speed = $this->_validate_speed($speed); - - $animations = "\t\t\t"; - - foreach ($params as $param => $value) - { - $animations .= $param.": '".$value."', "; - } - - $animations = substr($animations, 0, -2); // remove the last ", " - - if ($speed !== '') - { - $speed = ', '.$speed; - } - - if ($extra !== '') - { - $extra = ', '.$extra; - } - - return "$({$element}).animate({\n$animations\n\t\t}".$speed.$extra.');'; - } - - // -------------------------------------------------------------------- - - /** - * Fade In - * - * Outputs a jQuery hide event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - protected function _fadeIn($element = 'this', $speed = '', $callback = '') - { - $element = $this->_prep_element($element); - $speed = $this->_validate_speed($speed); - - if ($callback !== '') - { - $callback = ", function(){\n{$callback}\n}"; - } - - return "$({$element}).fadeIn({$speed}{$callback});"; - } - - // -------------------------------------------------------------------- - - /** - * Fade Out - * - * Outputs a jQuery hide event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - protected function _fadeOut($element = 'this', $speed = '', $callback = '') - { - $element = $this->_prep_element($element); - $speed = $this->_validate_speed($speed); - - if ($callback !== '') - { - $callback = ", function(){\n{$callback}\n}"; - } - - return '$('.$element.').fadeOut('.$speed.$callback.');'; - } - - // -------------------------------------------------------------------- - - /** - * Hide - * - * Outputs a jQuery hide action - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - protected function _hide($element = 'this', $speed = '', $callback = '') - { - $element = $this->_prep_element($element); - $speed = $this->_validate_speed($speed); - - if ($callback !== '') - { - $callback = ", function(){\n{$callback}\n}"; - } - - return "$({$element}).hide({$speed}{$callback});"; - } - - // -------------------------------------------------------------------- - - /** - * Remove Class - * - * Outputs a jQuery remove class event - * - * @param string $element - * @param string $class - * @return string - */ - protected function _removeClass($element = 'this', $class = '') - { - $element = $this->_prep_element($element); - return '$('.$element.').removeClass("'.$class.'");'; - } - - // -------------------------------------------------------------------- - - /** - * Slide Up - * - * Outputs a jQuery slideUp event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - protected function _slideUp($element = 'this', $speed = '', $callback = '') - { - $element = $this->_prep_element($element); - $speed = $this->_validate_speed($speed); - - if ($callback !== '') - { - $callback = ", function(){\n{$callback}\n}"; - } - - return '$('.$element.').slideUp('.$speed.$callback.');'; - } - - // -------------------------------------------------------------------- - - /** - * Slide Down - * - * Outputs a jQuery slideDown event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - protected function _slideDown($element = 'this', $speed = '', $callback = '') - { - $element = $this->_prep_element($element); - $speed = $this->_validate_speed($speed); - - if ($callback !== '') - { - $callback = ", function(){\n{$callback}\n}"; - } - - return '$('.$element.').slideDown('.$speed.$callback.');'; - } - - // -------------------------------------------------------------------- - - /** - * Slide Toggle - * - * Outputs a jQuery slideToggle event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - protected function _slideToggle($element = 'this', $speed = '', $callback = '') - { - $element = $this->_prep_element($element); - $speed = $this->_validate_speed($speed); - - if ($callback !== '') - { - $callback = ", function(){\n{$callback}\n}"; - } - - return '$('.$element.').slideToggle('.$speed.$callback.');'; - } - - // -------------------------------------------------------------------- - - /** - * Toggle - * - * Outputs a jQuery toggle event - * - * @param string - element - * @return string - */ - protected function _toggle($element = 'this') - { - $element = $this->_prep_element($element); - return '$('.$element.').toggle();'; - } - - // -------------------------------------------------------------------- - - /** - * Toggle Class - * - * Outputs a jQuery toggle class event - * - * @param string $element - * @param string $class - * @return string - */ - protected function _toggleClass($element = 'this', $class = '') - { - $element = $this->_prep_element($element); - return '$('.$element.').toggleClass("'.$class.'");'; - } - - // -------------------------------------------------------------------- - - /** - * Show - * - * Outputs a jQuery show event - * - * @param string - element - * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds - * @param string - Javascript callback function - * @return string - */ - protected function _show($element = 'this', $speed = '', $callback = '') - { - $element = $this->_prep_element($element); - $speed = $this->_validate_speed($speed); - - if ($callback !== '') - { - $callback = ", function(){\n{$callback}\n}"; - } - - return '$('.$element.').show('.$speed.$callback.');'; - } - - // -------------------------------------------------------------------- - - /** - * Updater - * - * An Ajax call that populates the designated DOM node with - * returned content - * - * @param string The element to attach the event to - * @param string the controller to run the call against - * @param string optional parameters - * @return string - */ - - protected function _updater($container = 'this', $controller = '', $options = '') - { - $container = $this->_prep_element($container); - $controller = (strpos('://', $controller) === FALSE) ? $controller : $this->CI->config->site_url($controller); - - // ajaxStart and ajaxStop are better choices here... but this is a stop gap - if ($this->CI->config->item('javascript_ajax_img') === '') - { - $loading_notifier = 'Loading...'; - } - else - { - $loading_notifier = '<img src="'.$this->CI->config->slash_item('base_url').$this->CI->config->item('javascript_ajax_img').'" alt="Loading" />'; - } - - $updater = '$('.$container.").empty();\n" // anything that was in... get it out - ."\t\t$(".$container.').prepend("'.$loading_notifier."\");\n"; // to replace with an image - - $request_options = ''; - if ($options !== '') - { - $request_options .= ', {' - .(is_array($options) ? "'".implode("', '", $options)."'" : "'".str_replace(':', "':'", $options)."'") - .'}'; - } - - return $updater."\t\t$($container).load('$controller'$request_options);"; - } - - // -------------------------------------------------------------------- - // Pre-written handy stuff - // -------------------------------------------------------------------- - - /** - * Zebra tables - * - * @param string $class - * @param string $odd - * @param string $hover - * @return string - */ - protected function _zebraTables($class = '', $odd = 'odd', $hover = '') - { - $class = ($class !== '') ? '.'.$class : ''; - $zebra = "\t\$(\"table{$class} tbody tr:nth-child(even)\").addClass(\"{$odd}\");"; - - $this->jquery_code_for_compile[] = $zebra; - - if ($hover !== '') - { - $hover = $this->hover("table{$class} tbody tr", "$(this).addClass('hover');", "$(this).removeClass('hover');"); - } - - return $zebra; - } - - // -------------------------------------------------------------------- - // Plugins - // -------------------------------------------------------------------- - - /** - * Corner Plugin - * - * @link http://www.malsup.com/jquery/corner/ - * @param string $element - * @param string $corner_style - * @return string - */ - public function corner($element = '', $corner_style = '') - { - // may want to make this configurable down the road - $corner_location = '/plugins/jquery.corner.js'; - - if ($corner_style !== '') - { - $corner_style = '"'.$corner_style.'"'; - } - - return '$('.$this->_prep_element($element).').corner('.$corner_style.');'; - } - - // -------------------------------------------------------------------- - - /** - * Modal window - * - * Load a thickbox modal window - * - * @param string $src - * @param bool $relative - * @return void - */ - public function modal($src, $relative = FALSE) - { - $this->jquery_code_for_load[] = $this->external($src, $relative); - } - - // -------------------------------------------------------------------- - - /** - * Effect - * - * Load an Effect library - * - * @param string $src - * @param bool $relative - * @return void - */ - public function effect($src, $relative = FALSE) - { - $this->jquery_code_for_load[] = $this->external($src, $relative); - } - - // -------------------------------------------------------------------- - - /** - * Plugin - * - * Load a plugin library - * - * @param string $src - * @param bool $relative - * @return void - */ - public function plugin($src, $relative = FALSE) - { - $this->jquery_code_for_load[] = $this->external($src, $relative); - } - - // -------------------------------------------------------------------- - - /** - * UI - * - * Load a user interface library - * - * @param string $src - * @param bool $relative - * @return void - */ - public function ui($src, $relative = FALSE) - { - $this->jquery_code_for_load[] = $this->external($src, $relative); - } - - // -------------------------------------------------------------------- - - /** - * Sortable - * - * Creates a jQuery sortable - * - * @param string $element - * @param array $options - * @return string - */ - public function sortable($element, $options = array()) - { - if (count($options) > 0) - { - $sort_options = array(); - foreach ($options as $k=>$v) - { - $sort_options[] = "\n\t\t".$k.': '.$v; - } - $sort_options = implode(',', $sort_options); - } - else - { - $sort_options = ''; - } - - return '$('.$this->_prep_element($element).').sortable({'.$sort_options."\n\t});"; - } - - // -------------------------------------------------------------------- - - /** - * Table Sorter Plugin - * - * @param string table name - * @param string plugin location - * @return string - */ - public function tablesorter($table = '', $options = '') - { - $this->jquery_code_for_compile[] = "\t$(".$this->_prep_element($table).').tablesorter('.$options.");\n"; - } - - // -------------------------------------------------------------------- - // Class functions - // -------------------------------------------------------------------- - - /** - * Add Event - * - * Constructs the syntax for an event, and adds to into the array for compilation - * - * @param string The element to attach the event to - * @param string The code to execute - * @param string The event to pass - * @return string - */ - protected function _add_event($element, $js, $event) - { - if (is_array($js)) - { - $js = implode("\n\t\t", $js); - } - - $event = "\n\t$(".$this->_prep_element($element).').'.$event."(function(){\n\t\t{$js}\n\t});\n"; - $this->jquery_code_for_compile[] = $event; - return $event; - } - - // -------------------------------------------------------------------- - - /** - * Compile - * - * As events are specified, they are stored in an array - * This function compiles them all for output on a page - * - * @param string $view_var - * @param bool $script_tags - * @return void - */ - protected function _compile($view_var = 'script_foot', $script_tags = TRUE) - { - // External references - $external_scripts = implode('', $this->jquery_code_for_load); - $this->CI->load->vars(array('library_src' => $external_scripts)); - - if (count($this->jquery_code_for_compile) === 0) - { - // no inline references, let's just return - return; - } - - // Inline references - $script = '$(document).ready(function() {'."\n" - .implode('', $this->jquery_code_for_compile) - .'});'; - - $output = ($script_tags === FALSE) ? $script : $this->inline($script); - - $this->CI->load->vars(array($view_var => $output)); - } - - // -------------------------------------------------------------------- - - /** - * Clear Compile - * - * Clears the array of script events collected for output - * - * @return void - */ - protected function _clear_compile() - { - $this->jquery_code_for_compile = array(); - } - - // -------------------------------------------------------------------- - - /** - * Document Ready - * - * A wrapper for writing document.ready() - * - * @param array $js - * @return void - */ - protected function _document_ready($js) - { - is_array($js) OR $js = array($js); - - foreach ($js as $script) - { - $this->jquery_code_for_compile[] = $script; - } - } - - // -------------------------------------------------------------------- - - /** - * Script Tag - * - * Outputs the script tag that loads the jquery.js file into an HTML document - * - * @param string $library_src - * @param bool $relative - * @return string - */ - public function script($library_src = '', $relative = FALSE) - { - $library_src = $this->external($library_src, $relative); - $this->jquery_code_for_load[] = $library_src; - return $library_src; - } - - // -------------------------------------------------------------------- - - /** - * Prep Element - * - * Puts HTML element in quotes for use in jQuery code - * unless the supplied element is the Javascript 'this' - * object, in which case no quotes are added - * - * @param string - * @return string - */ - protected function _prep_element($element) - { - if ($element !== 'this') - { - $element = '"'.$element.'"'; - } - - return $element; - } - - // -------------------------------------------------------------------- - - /** - * Validate Speed - * - * Ensures the speed parameter is valid for jQuery - * - * @param string - * @return string - */ - protected function _validate_speed($speed) - { - if (in_array($speed, array('slow', 'normal', 'fast'))) - { - return '"'.$speed.'"'; - } - elseif (preg_match('/[^0-9]/', $speed)) - { - return ''; - } - - return $speed; - } - -} diff --git a/system/libraries/Javascript/index.html b/system/libraries/Javascript/index.html deleted file mode 100644 index b702fbc39..000000000 --- a/system/libraries/Javascript/index.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <title>403 Forbidden</title> -</head> -<body> - -<p>Directory access is forbidden.</p> - -</body> -</html> diff --git a/system/libraries/Migration.php b/system/libraries/Migration.php index 45a3cbbce..2a87d9d7c 100644 --- a/system/libraries/Migration.php +++ b/system/libraries/Migration.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ @@ -96,9 +96,9 @@ class CI_Migration { /** * Migration basename regex * - * @var bool + * @var string */ - protected $_migration_regex = NULL; + protected $_migration_regex; /** * Error message @@ -217,31 +217,66 @@ class CI_Migration { if ($target_version > $current_version) { - // Moving Up $method = 'up'; } - else + elseif ($target_version < $current_version) { - // Moving Down, apply in reverse order $method = 'down'; + // We need this so that migrations are applied in reverse order krsort($migrations); } - - if (empty($migrations)) + else { + // Well, there's nothing to migrate then ... return TRUE; } - $previous = FALSE; - - // Validate all available migrations, and run the ones within our target range + // Validate all available migrations within our target range. + // + // Unfortunately, we'll have to use another loop to run them + // in order to avoid leaving the procedure in a broken state. + // + // See https://github.com/bcit-ci/CodeIgniter/issues/4539 + $pending = array(); foreach ($migrations as $number => $file) { + // Ignore versions out of our range. + // + // Because we've previously sorted the $migrations array depending on the direction, + // we can safely break the loop once we reach $target_version ... + if ($method === 'up') + { + if ($number <= $current_version) + { + continue; + } + elseif ($number > $target_version) + { + break; + } + } + else + { + if ($number > $current_version) + { + continue; + } + elseif ($number <= $target_version) + { + break; + } + } + // Check for sequence gaps - if ($this->_migration_type === 'sequential' && $previous !== FALSE && abs($number - $previous) > 1) + if ($this->_migration_type === 'sequential') { - $this->_error_string = sprintf($this->lang->line('migration_sequence_gap'), $number); - return FALSE; + if (isset($previous) && abs($number - $previous) > 1) + { + $this->_error_string = sprintf($this->lang->line('migration_sequence_gap'), $number); + return FALSE; + } + + $previous = $number; } include_once($file); @@ -253,27 +288,24 @@ class CI_Migration { $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class); return FALSE; } + elseif ( ! is_callable(array($class, $method))) + { + $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class); + return FALSE; + } - $previous = $number; + $pending[$number] = array($class, $method); + } - // Run migrations that are inside the target range - if ( - ($method === 'up' && $number > $current_version && $number <= $target_version) OR - ($method === 'down' && $number <= $current_version && $number > $target_version) - ) - { - $instance = new $class(); - if ( ! is_callable(array($instance, $method))) - { - $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class); - return FALSE; - } + // Now just run the necessary migrations + foreach ($pending as $number => $migration) + { + log_message('debug', 'Migrating '.$method.' from version '.$current_version.' to version '.$number); - log_message('debug', 'Migrating '.$method.' from version '.$current_version.' to version '.$number); - call_user_func(array($instance, $method)); - $current_version = $number; - $this->_update_version($current_version); - } + $migration[0] = new $migration[0]; + call_user_func($migration); + $current_version = $number; + $this->_update_version($current_version); } // This is necessary when moving down, since the the last migration applied @@ -285,7 +317,6 @@ class CI_Migration { } log_message('debug', 'Finished migrating to '.$current_version); - return $current_version; } diff --git a/system/libraries/Pagination.php b/system/libraries/Pagination.php index 5b3aa01f4..f26f8a4ed 100644 --- a/system/libraries/Pagination.php +++ b/system/libraries/Pagination.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Pagination * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/pagination.html + * @link https://codeigniter.com/user_guide/libraries/pagination.html */ class CI_Pagination { @@ -428,7 +428,7 @@ class CI_Pagination { { $get = $this->CI->input->get(); - // Unset the controll, method, old-school routing options + // Unset the control, method, old-school routing options unset($get['c'], $get['m'], $get[$this->query_string_segment]); } else @@ -497,7 +497,7 @@ class CI_Pagination { { $this->cur_page = $this->CI->input->get($this->query_string_segment); } - else + elseif (empty($this->cur_page)) { // Default to the last segment number if one hasn't been defined. if ($this->uri_segment === 0) @@ -513,6 +513,10 @@ class CI_Pagination { $this->cur_page = str_replace(array($this->prefix, $this->suffix), '', $this->cur_page); } } + else + { + $this->cur_page = (string) $this->cur_page; + } // If something isn't quite right, back to the default base page. if ( ! ctype_digit($this->cur_page) OR ($this->use_page_numbers && (int) $this->cur_page === 0)) @@ -571,7 +575,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) { @@ -592,11 +596,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) { @@ -614,7 +618,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; } } @@ -626,7 +630,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; @@ -637,7 +641,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; diff --git a/system/libraries/Parser.php b/system/libraries/Parser.php index 57981af95..fdd958b22 100644 --- a/system/libraries/Parser.php +++ b/system/libraries/Parser.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Parser * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/parser.html + * @link https://codeigniter.com/user_guide/libraries/parser.html */ class CI_Parser { diff --git a/system/libraries/Profiler.php b/system/libraries/Profiler.php index 1e464d8b0..e9e03cfe0 100644 --- a/system/libraries/Profiler.php +++ b/system/libraries/Profiler.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -50,7 +50,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Libraries * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/general/profiling.html + * @link https://codeigniter.com/user_guide/general/profiling.html */ class CI_Profiler { @@ -314,12 +314,14 @@ class CI_Profiler { foreach ($_GET as $key => $val) { - is_int($key) OR $key = "'".$key."'"; + is_int($key) OR $key = "'".htmlspecialchars($key, ENT_QUOTES, config_item('charset'))."'"; + $val = (is_array($val) OR is_object($val)) + ? '<pre>'.htmlspecialchars(print_r($val, TRUE), ENT_QUOTES, config_item('charset')) + : htmlspecialchars($val, ENT_QUOTES, config_item('charset')); $output .= '<tr><td style="width:50%;color:#000;background-color:#ddd;padding:5px;">$_GET[' .$key.'] </td><td style="width:50%;padding:5px;color:#cd6e00;font-weight:normal;background-color:#ddd;">' - .((is_array($val) OR is_object($val)) ? '<pre>'.htmlspecialchars(stripslashes(print_r($val, TRUE))).'</pre>' : htmlspecialchars(stripslashes($val))) - ."</td></tr>\n"; + .$val."</td></tr>\n"; } $output .= "</table>\n"; @@ -352,36 +354,26 @@ class CI_Profiler { foreach ($_POST as $key => $val) { - is_int($key) OR $key = "'".$key."'"; + is_int($key) OR $key = "'".htmlspecialchars($key, ENT_QUOTES, config_item('charset'))."'"; + $val = (is_array($val) OR is_object($val)) + ? '<pre>'.htmlspecialchars(print_r($val, TRUE), ENT_QUOTES, config_item('charset')) + : htmlspecialchars($val, ENT_QUOTES, config_item('charset')); $output .= '<tr><td style="width:50%;padding:5px;color:#000;background-color:#ddd;">$_POST[' - .$key.'] </td><td style="width:50%;padding:5px;color:#009900;font-weight:normal;background-color:#ddd;">'; - - if (is_array($val) OR is_object($val)) - { - $output .= '<pre>'.htmlspecialchars(stripslashes(print_r($val, TRUE))).'</pre>'; - } - else - { - $output .= htmlspecialchars(stripslashes($val)); - } - - $output .= "</td></tr>\n"; + .$key.'] </td><td style="width:50%;padding:5px;color:#009900;font-weight:normal;background-color:#ddd;">' + .$val."</td></tr>\n"; } foreach ($_FILES as $key => $val) { - is_int($key) OR $key = "'".$key."'"; + is_int($key) OR $key = "'".htmlspecialchars($key, ENT_QUOTES, config_item('charset'))."'"; + $val = (is_array($val) OR is_object($val)) + ? '<pre>'.htmlspecialchars(print_r($val, TRUE), ENT_QUOTES, config_item('charset')) + : htmlspecialchars($val, ENT_QUOTES, config_item('charset')); $output .= '<tr><td style="width:50%;padding:5px;color:#000;background-color:#ddd;">$_FILES[' - .$key.'] </td><td style="width:50%;padding:5px;color:#009900;font-weight:normal;background-color:#ddd;">'; - - if (is_array($val) OR is_object($val)) - { - $output .= '<pre>'.htmlspecialchars(stripslashes(print_r($val, TRUE))).'</pre>'; - } - - $output .= "</td></tr>\n"; + .$key.'] </td><td style="width:50%;padding:5px;color:#009900;font-weight:normal;background-color:#ddd;">' + .$val."</td></tr>\n"; } $output .= "</table>\n"; @@ -465,7 +457,7 @@ class CI_Profiler { foreach (array('HTTP_ACCEPT', 'HTTP_USER_AGENT', 'HTTP_CONNECTION', 'SERVER_PORT', 'SERVER_NAME', 'REMOTE_ADDR', 'SERVER_SOFTWARE', 'HTTP_ACCEPT_LANGUAGE', 'SCRIPT_NAME', 'REQUEST_METHOD',' HTTP_HOST', 'REMOTE_HOST', 'CONTENT_TYPE', 'SERVER_PROTOCOL', 'QUERY_STRING', 'HTTP_ACCEPT_ENCODING', 'HTTP_X_FORWARDED_FOR', 'HTTP_DNT') as $header) { - $val = isset($_SERVER[$header]) ? $_SERVER[$header] : ''; + $val = isset($_SERVER[$header]) ? htmlspecialchars($_SERVER[$header], ENT_QUOTES, config_item('charset')) : ''; $output .= '<tr><td style="vertical-align:top;width:50%;padding:5px;color:#900;background-color:#ddd;">' .$header.' </td><td style="width:50%;padding:5px;color:#000;background-color:#ddd;">'.$val."</td></tr>\n"; } diff --git a/system/libraries/Session/Session.php b/system/libraries/Session/Session.php index 05a470d86..3e4865cd1 100644 --- a/system/libraries/Session/Session.php +++ b/system/libraries/Session/Session.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 2.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Sessions * @author Andrey Andreev - * @link http://codeigniter.com/user_guide/libraries/sessions.html + * @link https://codeigniter.com/user_guide/libraries/sessions.html */ class CI_Session { @@ -57,6 +57,7 @@ class CI_Session { protected $_driver = 'files'; protected $_config; + protected $_sid_regexp; // ------------------------------------------------------------------------ @@ -91,6 +92,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'; } @@ -98,27 +100,12 @@ class CI_Session { // Configuration ... $this->_configure($params); + $this->_config['_sid_regexp'] = $this->_sid_regexp; $class = new $class($this->_config); if ($class instanceof SessionHandlerInterface) { - if (is_php('5.4')) - { - session_set_save_handler($class, TRUE); - } - else - { - session_set_save_handler( - array($class, 'open'), - array($class, 'close'), - array($class, 'read'), - array($class, 'write'), - array($class, 'destroy'), - array($class, 'gc') - ); - - register_shutdown_function('session_write_close'); - } + session_set_save_handler($class, TRUE); } else { @@ -130,7 +117,7 @@ class CI_Session { if (isset($_COOKIE[$this->_config['cookie_name']]) && ( ! is_string($_COOKIE[$this->_config['cookie_name']]) - OR ! preg_match('/^[0-9a-f]{40}$/', $_COOKIE[$this->_config['cookie_name']]) + OR ! preg_match('#\A'.$this->_sid_regexp.'\z#', $_COOKIE[$this->_config['cookie_name']]) ) ) { @@ -187,9 +174,6 @@ class CI_Session { */ protected function _ci_load_classes($driver) { - // PHP 5.4 compatibility - interface_exists('SessionHandlerInterface', FALSE) OR require_once(BASEPATH.'libraries/Session/SessionHandlerInterface.php'); - $prefix = config_item('subclass_prefix'); if ( ! class_exists('CI_Session_driver', FALSE)) @@ -231,7 +215,7 @@ class CI_Session { } } - if ( ! class_exists($prefix.$class) && file_exists($file_path = APPPATH.'libraries/Session/drivers/'.$prefix.$class.'.php')) + if ( ! class_exists($prefix.$class, FALSE) && file_exists($file_path = APPPATH.'libraries/Session/drivers/'.$prefix.$class.'.php')) { require_once($file_path); if (class_exists($prefix.$class, FALSE)) @@ -314,8 +298,82 @@ class CI_Session { ini_set('session.use_strict_mode', 1); ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); - ini_set('session.hash_function', 1); - ini_set('session.hash_bits_per_character', 4); + + $this->_configure_sid_length(); + } + + // ------------------------------------------------------------------------ + + /** + * Configure session ID length + * + * To make life easier, we used to force SHA-1 and 4 bits per + * character on everyone. And of course, someone was unhappy. + * + * Then PHP 7.1 broke backwards-compatibility because ext/session + * is such a mess that nobody wants to touch it with a pole stick, + * and the one guy who does, nobody has the energy to argue with. + * + * So we were forced to make changes, and OF COURSE something was + * going to break and now we have this pile of shit. -- Narf + * + * @return void + */ + protected function _configure_sid_length() + { + if (PHP_VERSION_ID < 70100) + { + $hash_function = ini_get('session.hash_function'); + if (ctype_digit($hash_function)) + { + if ($hash_function !== '1') + { + ini_set('session.hash_function', 1); + } + + $bits = 160; + } + elseif ( ! in_array($hash_function, hash_algos(), TRUE)) + { + ini_set('session.hash_function', 1); + $bits = 160; + } + elseif (($bits = strlen(hash($hash_function, 'dummy', false)) * 4) < 160) + { + ini_set('session.hash_function', 1); + $bits = 160; + } + + $bits_per_character = (int) ini_get('session.hash_bits_per_character'); + $sid_length = (int) ceil($bits / $bits_per_character); + } + else + { + $bits_per_character = (int) ini_get('session.sid_bits_per_character'); + $sid_length = (int) ini_get('session.sid_length'); + if (($bits = $sid_length * $bits_per_character) < 160) + { + // Add as many more characters as necessary to reach at least 160 bits + $sid_length += (int) ceil((160 % $bits) / $bits_per_character); + ini_set('session.sid_length', $sid_length); + } + } + + // Yes, 4,5,6 are the only known possible values as of 2016-10-27 + switch ($bits_per_character) + { + case 4: + $this->_sid_regexp = '[0-9a-f]'; + break; + case 5: + $this->_sid_regexp = '[0-9a-v]'; + break; + case 6: + $this->_sid_regexp = '[0-9a-zA-Z,-]'; + break; + } + + $this->_sid_regexp .= '{'.$sid_length.'}'; } // ------------------------------------------------------------------------ @@ -584,6 +642,24 @@ class CI_Session { // ------------------------------------------------------------------------ /** + * __isset() + * + * @param string $key 'session_id' or a session data key + * @return bool + */ + public function __isset($key) + { + if ($key === 'session_id') + { + return (session_status() === PHP_SESSION_ACTIVE); + } + + return isset($_SESSION[$key]); + } + + // ------------------------------------------------------------------------ + + /** * __set() * * @param string $key Session data key @@ -711,7 +787,7 @@ class CI_Session { * * Legacy CI_Session compatibility method * - * @param mixed $data Session data key(s) + * @param mixed $key Session data key(s) * @return void */ public function unset_userdata($key) diff --git a/system/libraries/Session/SessionHandlerInterface.php b/system/libraries/Session/SessionHandlerInterface.php deleted file mode 100644 index 9dab5ac07..000000000 --- a/system/libraries/Session/SessionHandlerInterface.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php -/** - * CodeIgniter - * - * An open source application development framework for PHP - * - * This content is released under the MIT License (MIT) - * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @package CodeIgniter - * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) - * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com - * @since Version 3.0.0 - * @filesource - */ -defined('BASEPATH') OR exit('No direct script access allowed'); - -/** - * SessionHandlerInterface - * - * PHP 5.4 compatibility interface - * - * @package CodeIgniter - * @subpackage Libraries - * @category Sessions - * @author Andrey Andreev - * @link http://codeigniter.com/user_guide/libraries/sessions.html - */ -interface SessionHandlerInterface { - - public function open($save_path, $name); - public function close(); - public function read($session_id); - public function write($session_id, $session_data); - public function destroy($session_id); - public function gc($maxlifetime); -} diff --git a/system/libraries/Session/Session_driver.php b/system/libraries/Session/Session_driver.php index 47376da5b..f32f14ae0 100644 --- a/system/libraries/Session/Session_driver.php +++ b/system/libraries/Session/Session_driver.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Sessions * @author Andrey Andreev - * @link http://codeigniter.com/user_guide/libraries/sessions.html + * @link https://codeigniter.com/user_guide/libraries/sessions.html */ abstract class CI_Session_driver implements SessionHandlerInterface { @@ -74,6 +74,18 @@ abstract class CI_Session_driver implements SessionHandlerInterface { */ protected $_session_id; + /** + * Success and failure return values + * + * Necessary due to a bug in all PHP 5 versions where return values + * from userspace handlers are not handled properly. PHP 7 fixes the + * bug, so we need to return different values depending on the version. + * + * @see https://wiki.php.net/rfc/session.user.return-value + * @var mixed + */ + protected $_success, $_failure; + // ------------------------------------------------------------------------ /** @@ -85,6 +97,17 @@ abstract class CI_Session_driver implements SessionHandlerInterface { public function __construct(&$params) { $this->_config =& $params; + + if (is_php('7')) + { + $this->_success = TRUE; + $this->_failure = FALSE; + } + else + { + $this->_success = 0; + $this->_failure = -1; + } } // ------------------------------------------------------------------------ @@ -145,4 +168,24 @@ abstract class CI_Session_driver implements SessionHandlerInterface { return TRUE; } + // ------------------------------------------------------------------------ + + /** + * Fail + * + * Drivers other than the 'files' one don't (need to) use the + * session.save_path INI setting, but that leads to confusing + * error messages emitted by PHP when open() or write() fail, + * as the message contains session.save_path ... + * To work around the problem, the drivers will call this method + * so that the INI is set just in time for the error message to + * be properly generated. + * + * @return mixed + */ + protected function _fail() + { + ini_set('session.save_path', config_item('sess_save_path')); + return $this->_failure; + } } diff --git a/system/libraries/Session/drivers/Session_database_driver.php b/system/libraries/Session/drivers/Session_database_driver.php index 1d01c2923..31f5a4663 100644 --- a/system/libraries/Session/drivers/Session_database_driver.php +++ b/system/libraries/Session/drivers/Session_database_driver.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Sessions * @author Andrey Andreev - * @link http://codeigniter.com/user_guide/libraries/sessions.html + * @link https://codeigniter.com/user_guide/libraries/sessions.html */ class CI_Session_database_driver extends CI_Session_driver implements SessionHandlerInterface { @@ -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".'); + } } // ------------------------------------------------------------------------ @@ -125,9 +128,12 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan */ public function open($save_path, $name) { - return empty($this->_db->conn_id) - ? (bool) $this->_db->db_connect() - : TRUE; + if (empty($this->_db->conn_id) && ! $this->_db->db_connect()) + { + return $this->_fail(); + } + + return $this->_success; } // ------------------------------------------------------------------------ @@ -144,6 +150,9 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan { if ($this->_get_lock($session_id) !== FALSE) { + // Prevent previous QB calls from messing with our queries + $this->_db->reset_query(); + // Needed by write() to detect session_regenerate_id() calls $this->_session_id = $session_id; @@ -157,8 +166,12 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']); } - if (($result = $this->_db->get()->row()) === NULL) + if ( ! ($result = $this->_db->get()) OR ($result = $result->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 ''; } @@ -192,21 +205,24 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan */ public function write($session_id, $session_data) { + // Prevent previous QB calls from messing with our queries + $this->_db->reset_query(); + + if ($this->_lock === FALSE) + { + return $this->_fail(); + } // Was the ID regenerated? - if ($session_id !== $this->_session_id) + elseif ($session_id !== $this->_session_id) { if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) { - return FALSE; + return $this->_fail(); } $this->_row_exists = FALSE; $this->_session_id = $session_id; } - elseif ($this->_lock === FALSE) - { - return FALSE; - } if ($this->_row_exists === FALSE) { @@ -220,10 +236,11 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan if ($this->_db->insert($this->_config['save_path'], $insert_data)) { $this->_fingerprint = md5($session_data); - return $this->_row_exists = TRUE; + $this->_row_exists = TRUE; + return $this->_success; } - return FALSE; + return $this->_fail(); } $this->_db->where('id', $session_id); @@ -243,10 +260,10 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan if ($this->_db->update($this->_config['save_path'], $update_data)) { $this->_fingerprint = md5($session_data); - return TRUE; + return $this->_success; } - return FALSE; + return $this->_fail(); } // ------------------------------------------------------------------------ @@ -260,9 +277,9 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan */ public function close() { - return ($this->_lock) - ? $this->_release_lock() - : TRUE; + return ($this->_lock && ! $this->_release_lock()) + ? $this->_fail() + : $this->_success; } // ------------------------------------------------------------------------ @@ -279,18 +296,28 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan { if ($this->_lock) { + // Prevent previous QB calls from messing with our queries + $this->_db->reset_query(); + $this->_db->where('id', $session_id); if ($this->_config['match_ip']) { $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']); } - return $this->_db->delete($this->_config['save_path']) - ? ($this->close() && $this->_cookie_destroy()) - : FALSE; + if ( ! $this->_db->delete($this->_config['save_path'])) + { + return $this->_fail(); + } } - return ($this->close() && $this->_cookie_destroy()); + if ($this->close() === $this->_success) + { + $this->_cookie_destroy(); + return $this->_success; + } + + return $this->_fail(); } // ------------------------------------------------------------------------ @@ -305,7 +332,12 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan */ public function gc($maxlifetime) { - return $this->_db->delete($this->_config['save_path'], 'timestamp < '.(time() - $maxlifetime)); + // Prevent previous QB calls from messing with our queries + $this->_db->reset_query(); + + return ($this->_db->delete($this->_config['save_path'], 'timestamp < '.(time() - $maxlifetime))) + ? $this->_success + : $this->_fail(); } // ------------------------------------------------------------------------ @@ -322,7 +354,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'] : ''); + $arg = md5($session_id.($this->_config['match_ip'] ? '_'.$_SERVER['REMOTE_ADDR'] : '')); if ($this->_db->query("SELECT GET_LOCK('".$arg."', 300) AS ci_session_lock")->row()->ci_session_lock) { $this->_lock = $arg; @@ -385,5 +417,4 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan return parent::_release_lock(); } - } diff --git a/system/libraries/Session/drivers/Session_files_driver.php b/system/libraries/Session/drivers/Session_files_driver.php index 45da91c46..6016e094e 100644 --- a/system/libraries/Session/drivers/Session_files_driver.php +++ b/system/libraries/Session/drivers/Session_files_driver.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Sessions * @author Andrey Andreev - * @link http://codeigniter.com/user_guide/libraries/sessions.html + * @link https://codeigniter.com/user_guide/libraries/sessions.html */ class CI_Session_files_driver extends CI_Session_driver implements SessionHandlerInterface { @@ -76,6 +76,20 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle */ protected $_file_new; + /** + * Validate SID regular expression + * + * @var string + */ + protected $_sid_regexp; + + /** + * mbstring.func_override flag + * + * @var bool + */ + protected static $func_override; + // ------------------------------------------------------------------------ /** @@ -95,8 +109,13 @@ 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'), '/\\'); } + + $this->_sid_regexp = $this->_config['_sid_regexp']; + + isset(self::$func_override) OR self::$func_override = (extension_loaded('mbstring') && ini_get('mbstring.func_override')); } // ------------------------------------------------------------------------ @@ -129,7 +148,7 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle .$name // we'll use the session cookie name as a prefix to avoid collisions .($this->_config['match_ip'] ? md5($_SERVER['REMOTE_ADDR']) : ''); - return TRUE; + return $this->_success; } // ------------------------------------------------------------------------ @@ -148,21 +167,12 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle // which re-reads session data if ($this->_file_handle === NULL) { - // Just using fopen() with 'c+b' mode would be perfect, but it is only - // available since PHP 5.2.6 and we have to set permissions for new files, - // so we'd have to hack around this ... - if (($this->_file_new = ! file_exists($this->_file_path.$session_id)) === TRUE) - { - if (($this->_file_handle = fopen($this->_file_path.$session_id, 'w+b')) === FALSE) - { - log_message('error', "Session: File '".$this->_file_path.$session_id."' doesn't exist and cannot be created."); - return FALSE; - } - } - elseif (($this->_file_handle = fopen($this->_file_path.$session_id, 'r+b')) === FALSE) + $this->_file_new = ! file_exists($this->_file_path.$session_id); + + if (($this->_file_handle = fopen($this->_file_path.$session_id, 'c+b')) === FALSE) { log_message('error', "Session: Unable to open file '".$this->_file_path.$session_id."'."); - return FALSE; + return $this->_failure; } if (flock($this->_file_handle, LOCK_EX) === FALSE) @@ -170,7 +180,7 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle log_message('error', "Session: Unable to obtain lock for file '".$this->_file_path.$session_id."'."); fclose($this->_file_handle); $this->_file_handle = NULL; - return FALSE; + return $this->_failure; } // Needed by write() to detect session_regenerate_id() calls @@ -183,13 +193,19 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle return ''; } } + // We shouldn't need this, but apparently we do ... + // See https://github.com/bcit-ci/CodeIgniter/issues/4039 + elseif ($this->_file_handle === FALSE) + { + return $this->_failure; + } else { rewind($this->_file_handle); } $session_data = ''; - for ($read = 0, $length = filesize($this->_file_path.$session_id); $read < $length; $read += strlen($buffer)) + for ($read = 0, $length = filesize($this->_file_path.$session_id); $read < $length; $read += self::strlen($buffer)) { if (($buffer = fread($this->_file_handle, $length - $read)) === FALSE) { @@ -218,20 +234,20 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle { // If the two IDs don't match, we have a session_regenerate_id() call // and we need to close the old handle and open a new one - if ($session_id !== $this->_session_id && ( ! $this->close() OR $this->read($session_id) === FALSE)) + if ($session_id !== $this->_session_id && ($this->close() === $this->_failure OR $this->read($session_id) === $this->_failure)) { - return FALSE; + return $this->_failure; } if ( ! is_resource($this->_file_handle)) { - return FALSE; + return $this->_failure; } elseif ($this->_fingerprint === md5($session_data)) { - return ($this->_file_new) - ? TRUE - : touch($this->_file_path.$session_id); + return ( ! $this->_file_new && ! touch($this->_file_path.$session_id)) + ? $this->_failure + : $this->_success; } if ( ! $this->_file_new) @@ -254,12 +270,12 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle { $this->_fingerprint = md5(substr($session_data, 0, $written)); log_message('error', 'Session: Unable to write data.'); - return FALSE; + return $this->_failure; } } $this->_fingerprint = md5($session_data); - return TRUE; + return $this->_success; } // ------------------------------------------------------------------------ @@ -279,10 +295,9 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle fclose($this->_file_handle); $this->_file_handle = $this->_file_new = $this->_session_id = NULL; - return TRUE; } - return TRUE; + return $this->_success; } // ------------------------------------------------------------------------ @@ -297,21 +312,33 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle */ public function destroy($session_id) { - if ($this->close()) + if ($this->close() === $this->_success) { - return file_exists($this->_file_path.$session_id) - ? (unlink($this->_file_path.$session_id) && $this->_cookie_destroy()) - : TRUE; + if (file_exists($this->_file_path.$session_id)) + { + $this->_cookie_destroy(); + return unlink($this->_file_path.$session_id) + ? $this->_success + : $this->_failure; + } + + return $this->_success; } elseif ($this->_file_path !== NULL) { clearstatcache(); - return file_exists($this->_file_path.$session_id) - ? (unlink($this->_file_path.$session_id) && $this->_cookie_destroy()) - : TRUE; + if (file_exists($this->_file_path.$session_id)) + { + $this->_cookie_destroy(); + return unlink($this->_file_path.$session_id) + ? $this->_success + : $this->_failure; + } + + return $this->_success; } - return FALSE; + return $this->_failure; } // ------------------------------------------------------------------------ @@ -329,15 +356,18 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle 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; + return $this->_failure; } $ts = time() - $maxlifetime; + $pattern = ($this->_config['match_ip'] === TRUE) + ? '[0-9a-f]{32}' + : ''; + $pattern = sprintf( - '/^%s[0-9a-f]{%d}$/', - preg_quote($this->_config['cookie_name'], '/'), - ($this->_config['match_ip'] === TRUE ? 72 : 40) + '#\A%s'.$pattern.$this->_sid_regexp.'\z#', + preg_quote($this->_config['cookie_name']) ); while (($file = readdir($directory)) !== FALSE) @@ -356,7 +386,21 @@ class CI_Session_files_driver extends CI_Session_driver implements SessionHandle closedir($directory); - return TRUE; + return $this->_success; } + // -------------------------------------------------------------------- + + /** + * Byte-safe strlen() + * + * @param string $str + * @return int + */ + protected static function strlen($str) + { + return (self::$func_override) + ? mb_strlen($str, '8bit') + : strlen($str); + } } diff --git a/system/libraries/Session/drivers/Session_memcached_driver.php b/system/libraries/Session/drivers/Session_memcached_driver.php index 97b860588..2556bf0f7 100644 --- a/system/libraries/Session/drivers/Session_memcached_driver.php +++ b/system/libraries/Session/drivers/Session_memcached_driver.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Sessions * @author Andrey Andreev - * @link http://codeigniter.com/user_guide/libraries/sessions.html + * @link https://codeigniter.com/user_guide/libraries/sessions.html */ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHandlerInterface { @@ -117,7 +117,7 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa { $this->_memcached = NULL; log_message('error', 'Session: Invalid Memcached save path format: '.$this->_config['save_path']); - return FALSE; + return $this->_fail(); } foreach ($matches as $match) @@ -142,10 +142,10 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa if (empty($server_list)) { log_message('error', 'Session: Memcached server pool is empty.'); - return FALSE; + return $this->_fail(); } - return TRUE; + return $this->_success; } // ------------------------------------------------------------------------ @@ -170,7 +170,7 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa return $session_data; } - return FALSE; + return $this->_fail(); } // ------------------------------------------------------------------------ @@ -186,40 +186,44 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa */ public function write($session_id, $session_data) { - if ( ! isset($this->_memcached)) + if ( ! isset($this->_memcached, $this->_lock_key)) { - return FALSE; + return $this->_fail(); } // Was the ID regenerated? elseif ($session_id !== $this->_session_id) { if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) { - return FALSE; + return $this->_fail(); } $this->_fingerprint = md5(''); $this->_session_id = $session_id; } - if (isset($this->_lock_key)) + $key = $this->_key_prefix.$session_id; + + $this->_memcached->replace($this->_lock_key, time(), 300); + if ($this->_fingerprint !== ($fingerprint = md5($session_data))) { - $this->_memcached->replace($this->_lock_key, time(), 300); - if ($this->_fingerprint !== ($fingerprint = md5($session_data))) + if ($this->_memcached->set($key, $session_data, $this->_config['expiration'])) { - if ($this->_memcached->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration'])) - { - $this->_fingerprint = $fingerprint; - return TRUE; - } - - return FALSE; + $this->_fingerprint = $fingerprint; + return $this->_success; } - return $this->_memcached->touch($this->_key_prefix.$session_id, $this->_config['expiration']); + return $this->_fail(); + } + elseif ( + $this->_memcached->touch($key, $this->_config['expiration']) + OR ($this->_memcached->getResultCode() === Memcached::RES_NOTFOUND && $this->_memcached->set($key, $session_data, $this->_config['expiration'])) + ) + { + return $this->_success; } - return FALSE; + return $this->_fail(); } // ------------------------------------------------------------------------ @@ -235,17 +239,17 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa { if (isset($this->_memcached)) { - isset($this->_lock_key) && $this->_memcached->delete($this->_lock_key); + $this->_release_lock(); if ( ! $this->_memcached->quit()) { - return FALSE; + return $this->_fail(); } $this->_memcached = NULL; - return TRUE; + return $this->_success; } - return FALSE; + return $this->_fail(); } // ------------------------------------------------------------------------ @@ -263,10 +267,11 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa if (isset($this->_memcached, $this->_lock_key)) { $this->_memcached->delete($this->_key_prefix.$session_id); - return $this->_cookie_destroy(); + $this->_cookie_destroy(); + return $this->_success; } - return FALSE; + return $this->_fail(); } // ------------------------------------------------------------------------ @@ -282,7 +287,7 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa public function gc($maxlifetime) { // Not necessary, Memcached takes care of that. - return TRUE; + return $this->_success; } // ------------------------------------------------------------------------ @@ -297,9 +302,17 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa */ protected function _get_lock($session_id) { - if (isset($this->_lock_key)) + // PHP 7 reuses the SessionHandler object on regeneration, + // so we need to check here if the lock key is for the + // correct session ID. + if ($this->_lock_key === $this->_key_prefix.$session_id.':lock') { - return $this->_memcached->replace($this->_lock_key, time(), 300); + if ( ! $this->_memcached->replace($this->_lock_key, time(), 300)) + { + return ($this->_memcached->getResultCode() === Memcached::RES_NOTFOUND) + ? $this->_memcached->set($this->_lock_key, time(), 300) + : FALSE; + } } // 30 attempts to obtain a lock, in case another request already has it @@ -359,5 +372,4 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa return TRUE; } - } diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php index b098cc441..5313de04c 100644 --- a/system/libraries/Session/drivers/Session_redis_driver.php +++ b/system/libraries/Session/drivers/Session_redis_driver.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Sessions * @author Andrey Andreev - * @link http://codeigniter.com/user_guide/libraries/sessions.html + * @link https://codeigniter.com/user_guide/libraries/sessions.html */ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandlerInterface { @@ -69,6 +69,13 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle */ protected $_lock_key; + /** + * Key exists flag + * + * @var bool + */ + protected $_key_exists = FALSE; + // ------------------------------------------------------------------------ /** @@ -85,27 +92,40 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle { log_message('error', 'Session: No Redis save path configured.'); } - elseif (preg_match('#(?:tcp://)?([^:?]+)(?:\:(\d+))?(\?.+)?#', $this->_config['save_path'], $matches)) + elseif (preg_match('#^unix://([^\?]+)(?<options>\?.+)?$#', $this->_config['save_path'], $matches)) + { + $save_path = array('path' => $matches[1]); + } + elseif (preg_match('#(?:tcp://)?([^:?]+)(?:\:(\d+))?(?<options>\?.+)?#', $this->_config['save_path'], $matches)) { - isset($matches[3]) OR $matches[3] = ''; // Just to avoid undefined index notices below - $this->_config['save_path'] = array( - 'host' => $matches[1], - 'port' => empty($matches[2]) ? NULL : $matches[2], - 'password' => preg_match('#auth=([^\s&]+)#', $matches[3], $match) ? $match[1] : NULL, - 'database' => preg_match('#database=(\d+)#', $matches[3], $match) ? (int) $match[1] : NULL, - 'timeout' => preg_match('#timeout=(\d+\.\d+)#', $matches[3], $match) ? (float) $match[1] : NULL + $save_path = array( + 'host' => $matches[1], + 'port' => empty($matches[2]) ? NULL : $matches[2], + 'timeout' => NULL // We always pass this to Redis::connect(), so it needs to exist ); - - preg_match('#prefix=([^\s&]+)#', $matches[3], $match) && $this->_key_prefix = $match[1]; } else { log_message('error', 'Session: Invalid Redis save path format: '.$this->_config['save_path']); } - if ($this->_config['match_ip'] === TRUE) + if (isset($save_path)) { - $this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':'; + if (isset($matches['options'])) + { + $save_path['password'] = preg_match('#auth=([^\s&]+)#', $matches['options'], $match) ? $match[1] : NULL; + $save_path['database'] = preg_match('#database=(\d+)#', $matches['options'], $match) ? (int) $match[1] : NULL; + $save_path['timeout'] = preg_match('#timeout=(\d+\.\d+)#', $matches['options'], $match) ? (float) $match[1] : NULL; + + preg_match('#prefix=([^\s&]+)#', $matches['options'], $match) && $this->_key_prefix = $match[1]; + } + + $this->_config['save_path'] = $save_path; + + if ($this->_config['match_ip'] === TRUE) + { + $this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':'; + } } } @@ -124,29 +144,40 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle { if (empty($this->_config['save_path'])) { - return FALSE; + return $this->_fail(); } $redis = new Redis(); - if ( ! $redis->connect($this->_config['save_path']['host'], $this->_config['save_path']['port'], $this->_config['save_path']['timeout'])) - { - log_message('error', 'Session: Unable to connect to Redis with the configured settings.'); - } - elseif (isset($this->_config['save_path']['password']) && ! $redis->auth($this->_config['save_path']['password'])) - { - log_message('error', 'Session: Unable to authenticate to Redis instance.'); - } - elseif (isset($this->_config['save_path']['database']) && ! $redis->select($this->_config['save_path']['database'])) + $connected = isset($this->_config['save_path']['path']) + ? $redis->connect($this->_config['save_path']['path']) + : $redis->connect( + $this->_config['save_path']['host'], + $this->_config['save_path']['port'], + $this->_config['save_path']['timeout'] + ); + + if ($connected) { - log_message('error', 'Session: Unable to select Redis database with index '.$this->_config['save_path']['database']); + if (isset($this->_config['save_path']['password']) && ! $redis->auth($this->_config['save_path']['password'])) + { + log_message('error', 'Session: Unable to authenticate to Redis instance.'); + } + elseif (isset($this->_config['save_path']['database']) && ! $redis->select($this->_config['save_path']['database'])) + { + log_message('error', 'Session: Unable to select Redis database with index '.$this->_config['save_path']['database']); + } + else + { + $this->_redis = $redis; + return $this->_success; + } } else { - $this->_redis = $redis; - return TRUE; + log_message('error', 'Session: Unable to connect to Redis with the configured settings.'); } - return FALSE; + return $this->_fail(); } // ------------------------------------------------------------------------ @@ -166,12 +197,17 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle // Needed by write() to detect session_regenerate_id() calls $this->_session_id = $session_id; - $session_data = (string) $this->_redis->get($this->_key_prefix.$session_id); + $session_data = $this->_redis->get($this->_key_prefix.$session_id); + + is_string($session_data) + ? $this->_key_exists = TRUE + : $session_data = ''; + $this->_fingerprint = md5($session_data); return $session_data; } - return FALSE; + return $this->_fail(); } // ------------------------------------------------------------------------ @@ -187,40 +223,38 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle */ public function write($session_id, $session_data) { - if ( ! isset($this->_redis)) + if ( ! isset($this->_redis, $this->_lock_key)) { - return FALSE; + return $this->_fail(); } // Was the ID regenerated? elseif ($session_id !== $this->_session_id) { if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) { - return FALSE; + return $this->_fail(); } - $this->_fingerprint = md5(''); + $this->_key_exists = FALSE; $this->_session_id = $session_id; } - if (isset($this->_lock_key)) + $this->_redis->setTimeout($this->_lock_key, 300); + if ($this->_fingerprint !== ($fingerprint = md5($session_data)) OR $this->_key_exists === FALSE) { - $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'])) { - if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration'])) - { - $this->_fingerprint = $fingerprint; - return TRUE; - } - - return FALSE; + $this->_fingerprint = $fingerprint; + $this->_key_exists = TRUE; + return $this->_success; } - return $this->_redis->setTimeout($this->_key_prefix.$session_id, $this->_config['expiration']); + return $this->_fail(); } - return FALSE; + return ($this->_redis->setTimeout($this->_key_prefix.$session_id, $this->_config['expiration'])) + ? $this->_success + : $this->_fail(); } // ------------------------------------------------------------------------ @@ -239,10 +273,10 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle try { if ($this->_redis->ping() === '+PONG') { - isset($this->_lock_key) && $this->_redis->delete($this->_lock_key); - if ( ! $this->_redis->close()) + $this->_release_lock(); + if ($this->_redis->close() === FALSE) { - return FALSE; + return $this->_fail(); } } } @@ -252,10 +286,10 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle } $this->_redis = NULL; - return TRUE; + return $this->_success; } - return TRUE; + return $this->_success; } // ------------------------------------------------------------------------ @@ -277,10 +311,11 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle log_message('debug', 'Session: Redis::delete() expected to return 1, got '.var_export($result, TRUE).' instead.'); } - return $this->_cookie_destroy(); + $this->_cookie_destroy(); + return $this->_success; } - return FALSE; + return $this->_fail(); } // ------------------------------------------------------------------------ @@ -296,7 +331,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle public function gc($maxlifetime) { // Not necessary, Redis takes care of that. - return TRUE; + return $this->_success; } // ------------------------------------------------------------------------ @@ -311,7 +346,10 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle */ protected function _get_lock($session_id) { - if (isset($this->_lock_key)) + // PHP 7 reuses the SessionHandler object on regeneration, + // so we need to check here if the lock key is for the + // correct session ID. + if ($this->_lock_key === $this->_key_prefix.$session_id.':lock') { return $this->_redis->setTimeout($this->_lock_key, 300); } diff --git a/system/libraries/Table.php b/system/libraries/Table.php index 2d9823093..dc309890b 100644 --- a/system/libraries/Table.php +++ b/system/libraries/Table.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.3.1 * @filesource */ @@ -46,7 +46,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category HTML Tables * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/table.html + * @link https://codeigniter.com/user_guide/libraries/table.html */ class CI_Table { @@ -277,6 +277,7 @@ class CI_Table { public function set_caption($caption) { $this->caption = $caption; + return $this; } // -------------------------------------------------------------------- @@ -426,6 +427,7 @@ class CI_Table { $this->rows = array(); $this->heading = array(); $this->auto_heading = TRUE; + $this->caption = NULL; return $this; } diff --git a/system/libraries/Trackback.php b/system/libraries/Trackback.php index 23bdbbd58..55e9a0ee6 100644 --- a/system/libraries/Trackback.php +++ b/system/libraries/Trackback.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -46,7 +46,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Trackbacks * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/trackback.html + * @link https://codeigniter.com/user_guide/libraries/trackback.html */ class CI_Trackback { @@ -370,7 +370,7 @@ class CI_Trackback { { $url = trim($url); - if (strpos($url, 'http') !== 0) + if (stripos($url, 'http') !== 0) { $url = 'http://'.$url; } diff --git a/system/libraries/Typography.php b/system/libraries/Typography.php index 3b6cb1645..b25d8fdaa 100644 --- a/system/libraries/Typography.php +++ b/system/libraries/Typography.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Helpers * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/typography.html + * @link https://codeigniter.com/user_guide/libraries/typography.html */ class CI_Typography { @@ -241,7 +241,7 @@ class CI_Typography { // Clean up stray paragraph tags that appear before block level elements '#<p></p><('.$this->block_elements.')#' => '<$1', - // Clean up stray non-breaking spaces preceeding block elements + // Clean up stray non-breaking spaces preceding block elements '#( \s*)+<('.$this->block_elements.')#' => ' <$2', // Replace the temporary markers we added earlier diff --git a/system/libraries/Unit_test.php b/system/libraries/Unit_test.php index 3f986f3e8..38e0fbd24 100644 --- a/system/libraries/Unit_test.php +++ b/system/libraries/Unit_test.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.3.1 * @filesource */ @@ -46,7 +46,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category UnitTesting * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/unit_testing.html + * @link https://codeigniter.com/user_guide/libraries/unit_testing.html */ class CI_Unit_test { @@ -154,7 +154,6 @@ class CI_Unit_test { 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); $extype = str_replace(array('true', 'false'), 'bool', str_replace('is_', '', $expected)); } @@ -291,7 +290,7 @@ class CI_Unit_test { { continue; } - elseif (in_array($key, array('test_name', 'test_datatype', 'test_res_datatype', 'result'), TRUE)) + elseif (in_array($key, array('test_name', 'test_datatype', 'res_datatype', 'result'), TRUE)) { if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val), FALSE))) { diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php index 8a2dec76a..3a1731e58 100644 --- a/system/libraries/Upload.php +++ b/system/libraries/Upload.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -44,7 +44,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Uploads * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/file_uploading.html + * @link https://codeigniter.com/user_guide/libraries/file_uploading.html */ class CI_Upload { @@ -286,7 +286,7 @@ class CI_Upload { /** * Constructor * - * @param array $props + * @param array $config * @return void */ public function __construct($config = array()) @@ -526,6 +526,12 @@ class CI_Upload { $this->file_name = preg_replace('/\s+/', '_', $this->file_name); } + if ($this->file_ext_tolower && ($ext_length = strlen($this->file_ext))) + { + // file_ext was previously lower-cased by a get_extension() call + $this->file_name = substr($this->file_name, 0, -$ext_length).$this->file_ext; + } + /* * Validate the file name * This function appends an number onto the end of @@ -595,7 +601,7 @@ class CI_Upload { 'file_type' => $this->file_type, 'file_path' => $this->upload_path, 'full_path' => $this->upload_path.$this->file_name, - 'raw_name' => str_replace($this->file_ext, '', $this->file_name), + 'raw_name' => substr($this->file_name, 0, -strlen($this->file_ext)), 'orig_name' => $this->orig_name, 'client_name' => $this->client_name, 'file_ext' => $this->file_ext, @@ -1077,16 +1083,27 @@ class CI_Upload { return FALSE; } - if (memory_get_usage() && ($memory_limit = ini_get('memory_limit'))) + if (memory_get_usage() && ($memory_limit = ini_get('memory_limit')) > 0) { - $memory_limit *= 1024 * 1024; - - // There was a bug/behavioural change in PHP 5.2, where numbers over one million get output - // into scientific notation. number_format() ensures this number is an integer - // http://bugs.php.net/bug.php?id=43053 - - $memory_limit = number_format(ceil(filesize($file) + $memory_limit), 0, '.', ''); + $memory_limit = str_split($memory_limit, strspn($memory_limit, '1234567890')); + if ( ! empty($memory_limit[1])) + { + switch ($memory_limit[1][0]) + { + case 'g': + case 'G': + $memory_limit[0] *= 1024 * 1024 * 1024; + break; + case 'm': + case 'M': + $memory_limit[0] *= 1024 * 1024; + break; + default: + break; + } + } + $memory_limit = (int) ceil(filesize($file) + $memory_limit[0]); ini_set('memory_limit', $memory_limit); // When an integer is used, the value is measured in bytes. - PHP.net } @@ -1201,10 +1218,13 @@ class CI_Upload { // We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii) $regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/'; - /* Fileinfo extension - most reliable method + /** + * Fileinfo extension - most reliable method * - * Unfortunately, prior to PHP 5.3 - it's only available as a PECL extension and the - * more convenient FILEINFO_MIME_TYPE flag doesn't exist. + * Apparently XAMPP, CentOS, cPanel and who knows what + * other PHP distribution channels EXPLICITLY DISABLE + * ext/fileinfo, which is otherwise enabled by default + * since PHP 5.3 ... */ if (function_exists('finfo_file')) { @@ -1239,9 +1259,7 @@ class CI_Upload { */ if (DIRECTORY_SEPARATOR !== '\\') { - $cmd = function_exists('escapeshellarg') - ? 'file --brief --mime '.escapeshellarg($file['tmp_name']).' 2>&1' - : 'file --brief --mime '.$file['tmp_name'].' 2>&1'; + $cmd = 'file --brief --mime '.escapeshellarg($file['tmp_name']).' 2>&1'; if (function_usable('exec')) { @@ -1258,7 +1276,7 @@ class CI_Upload { } } - if ( ! ini_get('safe_mode') && function_usable('shell_exec')) + if (function_usable('shell_exec')) { $mime = @shell_exec($cmd); if (strlen($mime) > 0) diff --git a/system/libraries/User_agent.php b/system/libraries/User_agent.php index 53d932a53..cda3ef0a0 100644 --- a/system/libraries/User_agent.php +++ b/system/libraries/User_agent.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -46,7 +46,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category User Agent * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/user_agent.html + * @link https://codeigniter.com/user_guide/libraries/user_agent.html */ class CI_User_agent { @@ -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/system/libraries/Xmlrpc.php b/system/libraries/Xmlrpc.php index 55555f56f..e025c4508 100644 --- a/system/libraries/Xmlrpc.php +++ b/system/libraries/Xmlrpc.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -51,7 +51,7 @@ if ( ! function_exists('xml_parser_create')) * @subpackage Libraries * @category XML-RPC * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html + * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class CI_Xmlrpc { @@ -238,7 +238,7 @@ class CI_Xmlrpc { public $result; /** - * XML-RPC Reponse + * XML-RPC Response * * @var array */ @@ -352,7 +352,7 @@ class CI_Xmlrpc { */ public function server($url, $port = 80, $proxy = FALSE, $proxy_port = 8080) { - if (strpos($url, 'http') !== 0) + if (stripos($url, 'http') !== 0) { $url = 'http://'.$url; } @@ -559,7 +559,7 @@ class CI_Xmlrpc { * * @category XML-RPC * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html + * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class XML_RPC_Client extends CI_Xmlrpc { @@ -735,6 +735,8 @@ class XML_RPC_Client extends CI_Xmlrpc .'Content-Length: '.strlen($msg->payload).$r.$r .$msg->payload; + stream_set_timeout($fp, $this->timeout); // set timeout for subsequent operations + for ($written = $timestamp = 0, $length = strlen($op); $written < $length; $written += $result) { if (($result = fwrite($fp, substr($op, $written))) === FALSE) @@ -753,9 +755,6 @@ class XML_RPC_Client extends CI_Xmlrpc $result = FALSE; break; } - - usleep(250000); - continue; } else { @@ -781,7 +780,7 @@ class XML_RPC_Client extends CI_Xmlrpc * * @category XML-RPC * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html + * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class XML_RPC_Response { @@ -837,9 +836,7 @@ class XML_RPC_Response { // error $this->errno = $code; - $this->errstr = htmlspecialchars($fstr, - (is_php('5.4') ? ENT_XML1 | ENT_NOQUOTES : ENT_NOQUOTES), - 'UTF-8'); + $this->errstr = htmlspecialchars($fstr, ENT_XML1 | ENT_NOQUOTES, 'UTF-8'); } elseif ( ! is_object($val)) { @@ -1030,7 +1027,7 @@ class XML_RPC_Response * * @category XML-RPC * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html + * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class XML_RPC_Message extends CI_Xmlrpc { @@ -1649,7 +1646,7 @@ class XML_RPC_Message extends CI_Xmlrpc * * @category XML-RPC * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html + * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class XML_RPC_Values extends CI_Xmlrpc { diff --git a/system/libraries/Xmlrpcs.php b/system/libraries/Xmlrpcs.php index 00d1feca6..21de937c8 100644 --- a/system/libraries/Xmlrpcs.php +++ b/system/libraries/Xmlrpcs.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -56,7 +56,7 @@ if ( ! class_exists('CI_Xmlrpc', FALSE)) * @subpackage Libraries * @category XML-RPC * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/xmlrpc.html + * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class CI_Xmlrpcs extends CI_Xmlrpc { @@ -339,11 +339,11 @@ class CI_Xmlrpcs extends CI_Xmlrpc { //------------------------------------- $method_parts = explode('.', $this->methods[$methName]['function']); - $objectCall = (isset($method_parts[1]) && $method_parts[1] !== ''); + $objectCall = ! empty($method_parts[1]); if ($system_call === TRUE) { - if ( ! is_callable(array($this,$method_parts[1]))) + if ( ! is_callable(array($this, $method_parts[1]))) { return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); } @@ -400,11 +400,11 @@ class CI_Xmlrpcs extends CI_Xmlrpc { } elseif ($this->object === FALSE) { - return get_instance()->$method_parts[1]($m); + return get_instance()->{$method_parts[1]}($m); } else { - return $this->object->$method_parts[1]($m); + return $this->object->{$method_parts[1]}($m); } } else diff --git a/system/libraries/Zip.php b/system/libraries/Zip.php index 3e98ac568..d6974b13a 100644 --- a/system/libraries/Zip.php +++ b/system/libraries/Zip.php @@ -6,7 +6,7 @@ * * This content is released under the MIT License (MIT) * - * Copyright (c) 2014 - 2015, British Columbia Institute of Technology + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,10 +28,10 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/) - * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/) + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License - * @link http://codeigniter.com + * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ @@ -50,7 +50,7 @@ defined('BASEPATH') OR exit('No direct script access allowed'); * @subpackage Libraries * @category Encryption * @author EllisLab Dev Team - * @link http://codeigniter.com/user_guide/libraries/zip.html + * @link https://codeigniter.com/user_guide/libraries/zip.html */ class CI_Zip { @@ -106,12 +106,21 @@ class CI_Zip { public $compression_level = 2; /** + * mbstring.func_override flag + * + * @var bool + */ + protected static $func_override; + + /** * Initialize zip compression class * * @return void */ public function __construct() { + isset(self::$func_override) OR self::$func_override = (extension_loaded('mbstring') && ini_get('mbstring.func_override')); + $this->now = time(); log_message('info', 'Zip Compression Class Initialized'); } @@ -182,7 +191,7 @@ class CI_Zip { .pack('V', 0) // crc32 .pack('V', 0) // compressed filesize .pack('V', 0) // uncompressed filesize - .pack('v', strlen($dir)) // length of pathname + .pack('v', self::strlen($dir)) // length of pathname .pack('v', 0) // extra field length .$dir // below is "data descriptor" segment @@ -197,7 +206,7 @@ class CI_Zip { .pack('V',0) // crc32 .pack('V',0) // compressed filesize .pack('V',0) // uncompressed filesize - .pack('v', strlen($dir)) // length of pathname + .pack('v', self::strlen($dir)) // length of pathname .pack('v', 0) // extra field length .pack('v', 0) // file comment length .pack('v', 0) // disk number start @@ -206,7 +215,7 @@ class CI_Zip { .pack('V', $this->offset) // relative offset of local header .$dir; - $this->offset = strlen($this->zipdata); + $this->offset = self::strlen($this->zipdata); $this->entries++; } @@ -255,10 +264,10 @@ class CI_Zip { { $filepath = str_replace('\\', '/', $filepath); - $uncompressed_size = strlen($data); + $uncompressed_size = self::strlen($data); $crc32 = crc32($data); - $gzdata = substr(gzcompress($data, $this->compression_level), 2, -4); - $compressed_size = strlen($gzdata); + $gzdata = self::substr(gzcompress($data, $this->compression_level), 2, -4); + $compressed_size = self::strlen($gzdata); $this->zipdata .= "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00" @@ -267,7 +276,7 @@ class CI_Zip { .pack('V', $crc32) .pack('V', $compressed_size) .pack('V', $uncompressed_size) - .pack('v', strlen($filepath)) // length of filename + .pack('v', self::strlen($filepath)) // length of filename .pack('v', 0) // extra field length .$filepath .$gzdata; // "file data" segment @@ -279,7 +288,7 @@ class CI_Zip { .pack('V', $crc32) .pack('V', $compressed_size) .pack('V', $uncompressed_size) - .pack('v', strlen($filepath)) // length of filename + .pack('v', self::strlen($filepath)) // length of filename .pack('v', 0) // extra field length .pack('v', 0) // file comment length .pack('v', 0) // disk number start @@ -288,7 +297,7 @@ class CI_Zip { .pack('V', $this->offset) // relative offset of local header .$filepath; - $this->offset = strlen($this->zipdata); + $this->offset = self::strlen($this->zipdata); $this->entries++; $this->file_num++; } @@ -401,8 +410,8 @@ class CI_Zip { .$this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00" .pack('v', $this->entries) // total # of entries "on this disk" .pack('v', $this->entries) // total # of entries overall - .pack('V', strlen($this->directory)) // size of central dir - .pack('V', strlen($this->zipdata)) // offset to start of central dir + .pack('V', self::strlen($this->directory)) // size of central dir + .pack('V', self::strlen($this->zipdata)) // offset to start of central dir ."\x00\x00"; // .zip file comment length } @@ -425,9 +434,9 @@ class CI_Zip { flock($fp, LOCK_EX); - for ($result = $written = 0, $data = $this->get_zip(), $length = strlen($data); $written < $length; $written += $result) + for ($result = $written = 0, $data = $this->get_zip(), $length = self::strlen($data); $written < $length; $written += $result) { - if (($result = fwrite($fp, substr($data, $written))) === FALSE) + if (($result = fwrite($fp, self::substr($data, $written))) === FALSE) { break; } @@ -481,4 +490,40 @@ class CI_Zip { return $this; } + // -------------------------------------------------------------------- + + /** + * Byte-safe strlen() + * + * @param string $str + * @return int + */ + protected static function strlen($str) + { + return (self::$func_override) + ? mb_strlen($str, '8bit') + : strlen($str); + } + + // -------------------------------------------------------------------- + + /** + * Byte-safe substr() + * + * @param string $str + * @param int $start + * @param int $length + * @return string + */ + protected static function substr($str, $start, $length = NULL) + { + if (self::$func_override) + { + return mb_substr($str, $start, $length, '8bit'); + } + + return isset($length) + ? substr($str, $start, $length) + : substr($str, $start); + } } |