<?php

/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\Honeypot;

use CodeIgniter\Config\BaseConfig;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Honeypot\Exceptions\HoneypotException;

/**
 * class Honeypot
 */
class Honeypot
{

	/**
	 * @var BaseConfig
	 */
	protected $config;

	//--------------------------------------------------------------------

	function __construct(BaseConfig $config)
	{
		$this->config = $config;

		if ($this->config->hidden === '')
		{
			throw HoneypotException::forNoHiddenValue();
		}

		if ($this->config->template === '')
		{
			throw HoneypotException::forNoTemplate();
		}

		if ($this->config->name === '')
		{
			throw HoneypotException::forNoNameField();
		}
	}

	//--------------------------------------------------------------------

	/**
	 * Checks the request if honeypot field has data.
	 *
	 * @param \CodeIgniter\HTTP\RequestInterface $request
	 */
	public function hasContent(RequestInterface $request)
	{
		return ! empty($request->getPost($this->config->name));
	}

	/**
	 * Attaches Honeypot template to response.
	 *
	 * @param \CodeIgniter\HTTP\ResponseInterface $response
	 */
	public function attachHoneypot(ResponseInterface $response)
	{
		$prep_field = $this->prepareTemplate($this->config->template);

		$body = $response->getBody();
		$body = str_ireplace('</form>', $prep_field, $body);
		$response->setBody($body);
	}

	/**
	 * Prepares the template by adding label
	 * content and field name.
	 *
	 * @param  string $template
	 * @return string
	 */
	protected function prepareTemplate(string $template): string
	{
		$template = str_ireplace('{label}', $this->config->label, $template);
		$template = str_ireplace('{name}', $this->config->name, $template);

		if ($this->config->hidden)
		{
			$template = '
' . $template . '
';
		}
		return $template;
	}

}


<?php

/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\Database\SQLite3;

use CodeIgniter\Database\BaseBuilder;

/**
 * Builder for SQLite3
 */
class Builder extends BaseBuilder
{

	/**
	 * Identifier escape character
	 *
	 * @var string
	 */
	protected $escapeChar = '`';

	/**
	 * Default installs of SQLite typically do not
	 * support limiting delete clauses.
	 *
	 * @var boolean
	 */
	protected $canLimitDeletes = false;

	/**
	 * Default installs of SQLite do no support
	 * limiting update queries in combo with WHERE.
	 *
	 * @var boolean
	 */
	protected $canLimitWhereUpdates = false;

	//--------------------------------------------------------------------

	/**
	 * Replace statement
	 *
	 * Generates a platform-specific replace string from the supplied data
	 *
	 * @param string    the table name
	 * @param array     the insert keys
	 * @param array     the insert values
	 *
	 * @return string
	 */
	protected function _replace(string $table, array $keys, array $values): string
	{
		return 'INSERT OR ' . parent::_replace($table, $keys, $values);
	}

	//--------------------------------------------------------------------

	/**
	 * Truncate statement
	 *
	 * Generates a platform-specific truncate string from the supplied data
	 *
	 * If the database does not support the TRUNCATE statement,
	 * then this method maps to 'DELETE FROM table'
	 *
	 * @param  string $table
	 * @return string
	 */
	protected function _truncate(string $table): string
	{
		return 'DELETE FROM ' . $table;
	}

	//--------------------------------------------------------------------

}


<?php
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\Database\SQLite3;

use CodeIgniter\Database\BaseResult;
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\Database\ResultInterface;

/**
 * Result for SQLite3
 */
class Result extends BaseResult implements ResultInterface
{

	/**
	 * Gets the number of fields in the result set.
	 *
	 * @return integer
	 */
	public function getFieldCount(): int
	{
		return $this->resultID->numColumns();
	}

	//--------------------------------------------------------------------

	/**
	 * Generates an array of column names in the result set.
	 *
	 * @return array
	 */
	public function getFieldNames(): array
	{
		$fieldNames = [];
		for ($i = 0, $c = $this->getFieldCount(); $i < $c; $i ++)
		{
			$fieldNames[] = $this->resultID->columnName($i);
		}

		return $fieldNames;
	}

	//--------------------------------------------------------------------

	/**
	 * Generates an array of objects representing field meta-data.
	 *
	 * @return array
	 */
	public function getFieldData(): array
	{
		static $data_types = [
			SQLITE3_INTEGER => 'integer',
			SQLITE3_FLOAT   => 'float',
			SQLITE3_TEXT    => 'text',
			SQLITE3_BLOB    => 'blob',
			SQLITE3_NULL    => 'null',
		];

		$retVal = [];

		for ($i = 0, $c = $this->getFieldCount(); $i < $c; $i ++)
		{
			$retVal[$i]             = new \stdClass();
			$retVal[$i]->name       = $this->resultID->columnName($i);
			$type                   = $this->resultID->columnType($i);
			$retVal[$i]->type       = isset($data_types[$type]) ? $data_types[$type] : $type;
			$retVal[$i]->max_length = null;
		}

		return $retVal;
	}

	//--------------------------------------------------------------------

	/**
	 * Frees the current result.
	 *
	 * @return void
	 */
	public function freeResult()
	{
		if (is_object($this->resultID))
		{
			$this->resultID->finalize();
			$this->resultID = false;
		}
	}

	//--------------------------------------------------------------------

	/**
	 * Moves the internal pointer to the desired offset. This is called
	 * internally before fetching results to make sure the result set
	 * starts at zero.
	 *
	 * @param integer $n
	 *
	 * @return mixed
	 * @throws \CodeIgniter\Database\Exceptions\DatabaseException
	 */
	public function dataSeek(int $n = 0)
	{
		if ($n !== 0)
		{
			throw new DatabaseException('SQLite3 doesn\'t support seeking to other offset.');
		}

		return $this->resultID->reset();
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the result set as an array.
	 *
	 * Overridden by driver classes.
	 *
	 * @return mixed
	 */
	protected function fetchAssoc()
	{
		return $this->resultID->fetchArray(SQLITE3_ASSOC);
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the result set as an object.
	 *
	 * Overridden by child classes.
	 *
	 * @param string $className
	 *
	 * @return object|boolean
	 */
	protected function fetchObject(string $className = 'stdClass')
	{
		// No native support for fetching rows as objects
		if (($row = $this->fetchAssoc()) === false)
		{
			return false;
		}
		elseif ($className === 'stdClass')
		{
			return (object) $row;
		}

		$classObj = new $className();
		$classSet = \Closure::bind(function ($key, $value) {
			$this->$key = $value;
		}, $classObj, $className
		);
		foreach (array_keys($row) as $key)
		{
			$classSet($key, $row[$key]);
		}
		return $classObj;
	}

	//--------------------------------------------------------------------
}


<?php
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014 - 2019, 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 - 2019, British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 1.3.1
 * @filesource
 */

namespace CodeIgniter\View;

use CodeIgniter\Database\BaseResult;

/**
 * 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
 */
class Table
{

	/**
	 * Data for table rows
	 *
	 * @var array
	 */
	public $rows = [];

	/**
	 * Data for table heading
	 *
	 * @var array
	 */
	public $heading = [];

	/**
	 * Data for table footing
	 *
	 * @var array
	 */
	public $footing = [];

	/**
	 * Whether or not to automatically create the table header
	 *
	 * @var boolean
	 */
	public $autoHeading = 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 $emptyCells = '';

	/**
	 * 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 = [])
	{
		// initialize config
		foreach ($config as $key => $val)
		{
			$this->template[$key] = $val;
		}
	}

	// --------------------------------------------------------------------

	/**
	 * Set the template
	 *
	 * @param  array $template
	 * @return boolean
	 */
	public function setTemplate($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 Table
	 */
	public function setHeading($args = [])
	{
		$this->heading = $this->_prepArgs(func_get_args());
		return $this;
	}

	/**
	 * Set the table footing
	 *
	 * Can be passed as an array or discreet params
	 *
	 * @param  mixed
	 * @return Table
	 */
	public function setFooting($args = [])
	{
		$this->footing = $this->_prepArgs(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  integer $columnLimit
	 * @return array
	 */
	public function makeColumns($array = [], $columnLimit = 0)
	{
		if (! is_array($array) || count($array) === 0 || ! is_int($columnLimit))
		{
			return false;
		}

		// Turn off the auto-heading feature since it's doubtful we
		// will want headings from a one-dimensional array
		$this->autoHeading = false;

		if ($columnLimit === 0)
		{
			return $array;
		}

		$new = [];
		do
		{
			$temp = array_splice($array, 0, $columnLimit);

			if (count($temp) < $columnLimit)
			{
				for ($i = count($temp); $i < $columnLimit; $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 Table
	 */
	public function setEmpty($value)
	{
		$this->emptyCells = $value;
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Add a table row
	 *
	 * Can be passed as an array or discreet params
	 *
	 * @param  mixed
	 * @return Table
	 */
	public function addRow($args = [])
	{
		$this->rows[] = $this->_prepArgs(func_get_args());
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Prep Args
	 *
	 * Ensures a standard associative array format for all cell data
	 *
	 * @param  array
	 * @return array
	 */
	protected function _prepArgs($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) || $args[$key] = ['data' => $val];
		}

		return $args;
	}

	// --------------------------------------------------------------------

	/**
	 * Add a table caption
	 *
	 * @param  string $caption
	 * @return Table
	 */
	public function setCaption($caption)
	{
		$this->caption = $caption;
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Generate the table
	 *
	 * @param  mixed $tableData
	 * @return string
	 */
	public function generate($tableData = null)
	{
		// The table data can optionally be passed to this function
		// either as a database result object or an array
		if (! empty($tableData))
		{
			if ($tableData instanceof BaseResult)
			{
				$this->_setFromDBResult($tableData);
			}
			elseif (is_array($tableData))
			{
				$this->_setFromArray($tableData);
			}
		}

		// 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->_compileTemplate();

		// 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 .= '
' . $this->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', '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)
			{
				// 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', 'emptyCells;
					}
					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;
			}
		}

		// Any table footing to display?
		if (! empty($this->footing))
		{
			$out .= $this->template['tfoot_open'] . $this->newline . $this->template['footing_row_start'] . $this->newline;

			foreach ($this->footing as $footing)
			{
				$temp = $this->template['footing_cell_start'];

				foreach ($footing as $key => $val)
				{
					if ($key !== 'data')
					{
						$temp = str_replace('<th', 'template['footing_cell_end'];
			}

			$out .= $this->template['footing_row_end'] . $this->newline . $this->template['tfoot_close'] . $this->newline;
		}

		$out .= $this->template['tbody_close'] . $this->newline;

		// And finally, close off the table
		$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 Table
	 */
	public function clear()
	{
		$this->rows        = [];
		$this->heading     = [];
		$this->footing     = [];
		$this->autoHeading = true;
		$this->caption     = null;
		return $this;
	}

	// --------------------------------------------------------------------

	/**
	 * Set table data from a database result object
	 *
	 * @param  BaseResult $object Database result object
	 * @return void
	 */
	protected function _setFromDBResult($object)
	{
		// First generate the headings from the table column names
		if ($this->autoHeading === true && empty($this->heading))
		{
			$this->heading = $this->_prepArgs($object->getFieldNames());
		}

		foreach ($object->getResultArray() as $row)
		{
			$this->rows[] = $this->_prepArgs($row);
		}
	}

	// --------------------------------------------------------------------

	/**
	 * Set table data from an array
	 *
	 * @param  array $data
	 * @return void
	 */
	protected function _setFromArray($data)
	{
		if ($this->autoHeading === true && empty($this->heading))
		{
			$this->heading = $this->_prepArgs(array_shift($data));
		}

		foreach ($data as &$row)
		{
			$this->rows[] = $this->_prepArgs($row);
		}
	}

	// --------------------------------------------------------------------

	/**
	 * Compile Template
	 *
	 * @return void
	 */
	protected function _compileTemplate()
	{
		if ($this->template === null)
		{
			$this->template = $this->_defaultTemplate();
			return;
		}

		$this->temp = $this->_defaultTemplate();
		foreach (['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 _defaultTemplate()
	{
		return [
			'table_open'         => '
',
			'thead_open'         => '
',
			'thead_close'        => '
',
			'heading_row_start'  => '
',
			'heading_row_end'    => '
',
			'heading_cell_start' => '',
			'heading_cell_end'   => '	',
			'tfoot_open'         => '
',
			'tfoot_close'        => '
',
			'footing_row_start'  => '
',
			'footing_row_end'    => '
',
			'footing_cell_start' => '',
			'footing_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'        => '
',
		];
	}

	// --------------------------------------------------------------------
}


<?php
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\Cache\Handlers;

use CodeIgniter\Cache\CacheInterface;
use CodeIgniter\Exceptions\CriticalError;

/**
 * Mamcached cache handler
 */
class MemcachedHandler implements CacheInterface
{

	/**
	 * Prefixed to all cache names.
	 *
	 * @var string
	 */
	protected $prefix;

	/**
	 * The memcached object
	 *
	 * @var \Memcached|\Memcache
	 */
	protected $memcached;

	/**
	 * Memcached Configuration
	 *
	 * @var array
	 */
	protected $config = [
		'host'   => '127.0.0.1',
		'port'   => 11211,
		'weight' => 1,
		'raw'    => false,
	];

	//--------------------------------------------------------------------

	public function __construct($config)
	{
		$config       = (array)$config;
		$this->prefix = $config['prefix'] ?? '';

		if (! empty($config))
		{
			$this->config = array_merge($this->config, $config['memcached']);
		}
	}

	/**
	 * Class destructor
	 *
	 * Closes the connection to Memcache(d) if present.
	 */
	public function __destruct()
	{
		if ($this->memcached instanceof \Memcached)
		{
			$this->memcached->quit();
		}
		elseif ($this->memcached instanceof \Memcache)
		{
			$this->memcached->close();
		}
	}

	//--------------------------------------------------------------------

	/**
	 * Takes care of any handler-specific setup that must be done.
	 */
	public function initialize()
	{
		if (class_exists('\Memcached'))
		{
			$this->memcached = new \Memcached();
			if ($this->config['raw'])
			{
				$this->memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
			}
		}
		elseif (class_exists('\Memcache'))
		{
			$this->memcached = new \Memcache();
		}
		else
		{
			throw new CriticalError('Cache: Not support Memcache(d) extension.');
		}

		if ($this->memcached instanceof \Memcached)
		{
			$this->memcached->addServer(
					$this->config['host'], $this->config['port'], $this->config['weight']
			);
		}
		elseif ($this->memcached instanceof \Memcache)
		{
			// Third parameter is persistence and defaults to TRUE.
			$this->memcached->addServer(
					$this->config['host'], $this->config['port'], true, $this->config['weight']
			);
		}
	}

	//--------------------------------------------------------------------

	/**
	 * Attempts to fetch an item from the cache store.
	 *
	 * @param string $key Cache item name
	 *
	 * @return mixed
	 */
	public function get(string $key)
	{
		$key = $this->prefix . $key;

		if ($this->memcached instanceof \Memcached)
		{
			$data = $this->memcached->get($key);

			// check for unmatched key
			if ($this->memcached->getResultCode()==\Memcached::RES_NOTFOUND)
			{
				return null;
			}
		}
		elseif ($this->memcached instanceof \Memcache)
		{
			$flags = false;
			$data = $this->memcached->get($key, $flags);

			// check for unmatched key (i.e. $flags is untouched)
			if ($flags===false)
			{
				return null;
			}
		}

		return is_array($data) ? $data[0] : $data;
	}

	//--------------------------------------------------------------------

	/**
	 * Saves an item to the cache store.
	 *
	 * @param string  $key   Cache item name
	 * @param mixed   $value The data to save
	 * @param integer $ttl   Time To Live, in seconds (default 60)
	 *
	 * @return mixed
	 */
	public function save(string $key, $value, int $ttl = 60)
	{
		$key = $this->prefix . $key;

		if (! $this->config['raw'])
		{
			$value = [
				$value,
				time(),
				$ttl,
			];
		}

		if ($this->memcached instanceof \Memcached)
		{
			return $this->memcached->set($key, $value, $ttl);
		}
		elseif ($this->memcached instanceof \Memcache)
		{
			return $this->memcached->set($key, $value, 0, $ttl);
		}

		return false;
	}

	//--------------------------------------------------------------------

	/**
	 * Deletes a specific item from the cache store.
	 *
	 * @param string $key Cache item name
	 *
	 * @return mixed
	 */
	public function delete(string $key)
	{
		$key = $this->prefix . $key;

		return $this->memcached->delete($key);
	}

	//--------------------------------------------------------------------

	/**
	 * Performs atomic incrementation of a raw stored value.
	 *
	 * @param string  $key    Cache ID
	 * @param integer $offset Step/value to increase by
	 *
	 * @return mixed
	 */
	public function increment(string $key, int $offset = 1)
	{
		if (! $this->config['raw'])
		{
			return false;
		}

		$key = $this->prefix . $key;

		return $this->memcached->increment($key, $offset, $offset, 60);
	}

	//--------------------------------------------------------------------

	/**
	 * Performs atomic decrementation of a raw stored value.
	 *
	 * @param string  $key    Cache ID
	 * @param integer $offset Step/value to increase by
	 *
	 * @return mixed
	 */
	public function decrement(string $key, int $offset = 1)
	{
		if (! $this->config['raw'])
		{
			return false;
		}

		$key = $this->prefix . $key;

		//FIXME: third parameter isn't other handler actions.
		return $this->memcached->decrement($key, $offset, $offset, 60);
	}

	//--------------------------------------------------------------------

	/**
	 * Will delete all items in the entire cache.
	 *
	 * @return mixed
	 */
	public function clean()
	{
		return $this->memcached->flush();
	}

	//--------------------------------------------------------------------

	/**
	 * Returns information on the entire cache.
	 *
	 * The information returned and the structure of the data
	 * varies depending on the handler.
	 *
	 * @return mixed
	 */
	public function getCacheInfo()
	{
		return $this->memcached->getStats();
	}

	//--------------------------------------------------------------------

	/**
	 * Returns detailed information about the specific item in the cache.
	 *
	 * @param string $key Cache item name.
	 *
	 * @return mixed
	 */
	public function getMetaData(string $key)
	{
		$key = $this->prefix . $key;

		$stored = $this->memcached->get($key);

		// if not an array, don't try to count for PHP7.2
		if (! is_array($stored) || count($stored) !== 3)
		{
			return false;
		}

		list($data, $time, $ttl) = $stored;

		return [
			'expire' => $time + $ttl,
			'mtime'  => $time,
			'data'   => $data,
		];
	}

	//--------------------------------------------------------------------

	/**
	 * Determines if the driver is supported on this system.
	 *
	 * @return boolean
	 */
	public function isSupported(): bool
	{
		return (extension_loaded('memcached') || extension_loaded('memcache'));
	}

}


<?php

/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\HTTP\Files;

use CodeIgniter\Files\File;
use CodeIgniter\HTTP\Exceptions\HTTPException;
use Config\Mimes;
use Exception;

/**
 * Value object representing a single file uploaded through an
 * HTTP request. Used by the IncomingRequest class to
 * provide files.
 *
 * Typically, implementors will extend the SplFileInfo class.
 *
 * @package CodeIgniter\HTTP
 */
class UploadedFile extends File implements UploadedFileInterface
{

	/**
	 * The path to the temporary file.
	 *
	 * @var string
	 */
	protected $path;

	/**
	 * The original filename as provided by the client.
	 *
	 * @var string
	 */
	protected $originalName;

	/**
	 * The filename given to a file during a move.
	 *
	 * @var string
	 */
	protected $name;

	/**
	 * The type of file as provided by PHP
	 *
	 * @var string
	 */
	protected $originalMimeType;

	/**
	 * The error constant of the upload
	 * (one of PHP's UPLOADERRXXX constants)
	 *
	 * @var integer
	 */
	protected $error;

	/**
	 * Whether the file has been moved already or not.
	 *
	 * @var boolean
	 */
	protected $hasMoved = false;

	//--------------------------------------------------------------------

	/**
	 * Accepts the file information as would be filled in from the $_FILES array.
	 *
	 * @param string  $path         The temporary location of the uploaded file.
	 * @param string  $originalName The client-provided filename.
	 * @param string  $mimeType     The type of file as provided by PHP
	 * @param integer $size         The size of the file, in bytes
	 * @param integer $error        The error constant of the upload (one of PHP's UPLOADERRXXX constants)
	 */
	public function __construct(string $path, string $originalName, string $mimeType = null, int $size = null, int $error = null)
	{
		$this->path             = $path;
		$this->name             = $originalName;
		$this->originalName     = $originalName;
		$this->originalMimeType = $mimeType;
		$this->size             = $size;
		$this->error            = $error;

		parent::__construct($path, false);
	}

	//--------------------------------------------------------------------

	/**
	 * Move the uploaded file to a new location.
	 *
	 * $targetPath may be an absolute path, or a relative path. If it is a
	 * relative path, resolution should be the same as used by PHP's rename()
	 * function.
	 *
	 * The original file MUST be removed on completion.
	 *
	 * If this method is called more than once, any subsequent calls MUST raise
	 * an exception.
	 *
	 * When used in an SAPI environment where $_FILES is populated, when writing
	 * files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be
	 * used to ensure permissions and upload status are verified correctly.
	 *
	 * If you wish to move to a stream, use getStream(), as SAPI operations
	 * cannot guarantee writing to stream destinations.
	 *
	 * @see http://php.net/is_uploaded_file
	 * @see http://php.net/move_uploaded_file
	 *
	 * @param string  $targetPath Path to which to move the uploaded file.
	 * @param string  $name       the name to rename the file to.
	 * @param boolean $overwrite  State for indicating whether to overwrite the previously generated file with the same
	 *                            name or not.
	 *
	 * @return boolean
	 *
	 * @throws \InvalidArgumentException if the $path specified is invalid.
	 * @throws \RuntimeException on any error during the move operation.
	 * @throws \RuntimeException on the second or subsequent call to the method.
	 */
	public function move(string $targetPath, string $name = null, bool $overwrite = false)
	{
		$targetPath = $this->setPath($targetPath); //set the target path

		if ($this->hasMoved)
		{
			throw HTTPException::forAlreadyMoved();
		}

		if (! $this->isValid())
		{
			throw HTTPException::forInvalidFile();
		}

		$targetPath  = rtrim($targetPath, '/') . '/';
		$name        = is_null($name) ? $this->getName() : $name;
		$destination = $overwrite ? $targetPath . $name : $this->getDestination($targetPath . $name);

		try
		{
			move_uploaded_file($this->path, $destination);
		}
		catch (Exception $e)
		{
			$error = error_get_last();
			throw HTTPException::forMoveFailed(basename($this->path), $targetPath, strip_tags($error['message']));
		}

		@chmod($targetPath, 0777 & ~umask());

		// Success, so store our new information
		$this->path     = $targetPath;
		$this->name     = basename($destination);
		$this->hasMoved = true;

		return true;
	}

	/**
	 * create file target path if
	 * the set path does not exist
	 *
	 * @param string $path
	 *
	 * @return string The path set or created.
	 */
	protected function setPath(string $path): string
	{
		if (! is_dir($path))
		{
			mkdir($path, 0777, true);
			//create the index.html file
			if (! is_file($path . 'index.html'))
			{
				$file = fopen($path . 'index.html', 'x+');
				fclose($file);
			}
		}
		return $path;
	}

	//--------------------------------------------------------------------

	/**
	 * Returns whether the file has been moved or not. If it has,
	 * the move() method will not work and certain properties, like
	 * the tempName, will no longer be available.
	 *
	 * @return boolean
	 */
	public function hasMoved(): bool
	{
		return $this->hasMoved;
	}

	//--------------------------------------------------------------------

	/**
	 * Retrieve the error associated with the uploaded file.
	 *
	 * The return value MUST be one of PHP's UPLOAD_ERR_XXX constants.
	 *
	 * If the file was uploaded successfully, this method MUST return
	 * UPLOAD_ERR_OK.
	 *
	 * Implementations SHOULD return the value stored in the "error" key of
	 * the file in the $_FILES array.
	 *
	 * @see    http://php.net/manual/en/features.file-upload.errors.php
	 * @return integer One of PHP's UPLOAD_ERR_XXX constants.
	 */
	public function getError(): int
	{
		if (is_null($this->error))
		{
			return UPLOAD_ERR_OK;
		}

		return $this->error;
	}

	//--------------------------------------------------------------------

	/**
	 * Get error string
	 *
	 * @return string
	 */
	public function getErrorString(): string
	{
		$errors = [
			UPLOAD_ERR_OK         => lang('HTTP.uploadErrOk'),
			UPLOAD_ERR_INI_SIZE   => lang('HTTP.uploadErrIniSize'),
			UPLOAD_ERR_FORM_SIZE  => lang('HTTP.uploadErrFormSize'),
			UPLOAD_ERR_PARTIAL    => lang('HTTP.uploadErrPartial'),
			UPLOAD_ERR_NO_FILE    => lang('HTTP.uploadErrNoFile'),
			UPLOAD_ERR_CANT_WRITE => lang('HTTP.uploadErrCantWrite'),
			UPLOAD_ERR_NO_TMP_DIR => lang('HTTP.uploadErrNoTmpDir'),
			UPLOAD_ERR_EXTENSION  => lang('HTTP.uploadErrExtension'),
		];

		$error = is_null($this->error) ? UPLOAD_ERR_OK : $this->error;

		return sprintf($errors[$error] ?? lang('HTTP.uploadErrUnknown'), $this->getName());
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the mime type as provided by the client.
	 * This is NOT a trusted value.
	 * For a trusted version, use getMimeType() instead.
	 *
	 * @return string The media type sent by the client or null if none was provided.
	 */
	public function getClientMimeType(): string
	{
		return $this->originalMimeType;
	}

	//--------------------------------------------------------------------

	/**
	 * Retrieve the filename. This will typically be the filename sent
	 * by the client, and should not be trusted. If the file has been
	 * moved, this will return the final name of the moved file.
	 *
	 * @return string The filename sent by the client or null if none was provided.
	 */
	public function getName(): string
	{
		return $this->name;
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the name of the file as provided by the client during upload.
	 *
	 * @return string
	 */
	public function getClientName(): string
	{
		return $this->originalName;
	}

	//--------------------------------------------------------------------

	/**
	 * Gets the temporary filename where the file was uploaded to.
	 *
	 * @return string
	 */
	public function getTempName(): string
	{
		return $this->path;
	}

	//--------------------------------------------------------------------

	/**
	 * Overrides SPLFileInfo's to work with uploaded files, since
	 * the temp file that's been uploaded doesn't have an extension.
	 *
	 * Is simply an alias for guessExtension for a safer method
	 * than simply relying on the provided extension.
	 * Additionally it will return clientExtension in case if there are
	 * other extensions with the same mime type.
	 */
	public function getExtension(): string
	{
		return $this->guessExtension();
	}

	/**
	 * Attempts to determine the best file extension.
	 *
	 * @return string|null
	 */
	public function guessExtension(): ?string
	{
		return Mimes::guessExtensionFromType($this->getClientMimeType(), $this->getClientExtension());
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the original file extension, based on the file name that
	 * was uploaded. This is NOT a trusted source.
	 * For a trusted version, use guessExtension() instead.
	 *
	 * @return string
	 */
	public function getClientExtension(): string
	{
		return pathinfo($this->originalName, PATHINFO_EXTENSION) ?? '';
	}

	//--------------------------------------------------------------------

	/**
	 * Returns whether the file was uploaded successfully, based on whether
	 * it was uploaded via HTTP and has no errors.
	 *
	 * @return boolean
	 */
	public function isValid(): bool
	{
		return is_uploaded_file($this->path) && $this->error === UPLOAD_ERR_OK;
	}

	/**
	 * Save the uploaded file to a new location.
	 *
	 * By default, upload files are saved in writable/uploads directory. The YYYYMMDD folder
	 * and random file name will be created.
	 *
	 * @param  string $folderName the folder name to writable/uploads directory.
	 * @param  string $fileName   the name to rename the file to.
	 * @return string file full path
	 */
	public function store(string $folderName = null, string $fileName = null): string
	{
		$folderName = $folderName ?? date('Ymd');
		$fileName   = $fileName ?? $this->getRandomName();

		// Move the uploaded file to a new location.
		return ($this->move(WRITEPATH . 'uploads/' . $folderName, $fileName)) ?
				$folderName . DIRECTORY_SEPARATOR . $this->name : null;
	}

	//--------------------------------------------------------------------
}


<?php
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\Commands\Database;

use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use Config\Autoload;
use Config\Migrations;

/**
 * Creates a new migration file.
 *
 * @package CodeIgniter\Commands
 */
class CreateMigration extends BaseCommand
{

	/**
	 * The group the command is lumped under
	 * when listing commands.
	 *
	 * @var string
	 */
	protected $group = 'Database';

	/**
	 * The Command's name
	 *
	 * @var string
	 */
	protected $name = 'migrate:create';

	/**
	 * the Command's short description
	 *
	 * @var string
	 */
	protected $description = 'Creates a new migration file.';

	/**
	 * the Command's usage
	 *
	 * @var string
	 */
	protected $usage = 'migrate:create [migration_name] [Options]';

	/**
	 * the Command's Arguments
	 *
	 * @var array
	 */
	protected $arguments = [
		'migration_name' => 'The migration file name',
	];

	/**
	 * the Command's Options
	 *
	 * @var array
	 */
	protected $options = [
		'-n' => 'Set migration namespace',
	];

	/**
	 * Creates a new migration file with the current timestamp.
	 *
	 * @param array $params
	 */
	public function run(array $params = [])
	{
		$name = array_shift($params);

		if (empty($name))
		{
			$name = CLI::prompt(lang('Migrations.nameMigration'));
		}

		if (empty($name))
		{
			CLI::error(lang('Migrations.badCreateName'));
			return;
		}
		$ns       = $params['-n'] ?? CLI::getOption('n');
		$homepath = APPPATH;

		if (! empty($ns))
		{
			// Get all namespaces form  PSR4 paths.
			$config     = new Autoload();
			$namespaces = $config->psr4;

			foreach ($namespaces as $namespace => $path)
			{
				if ($namespace === $ns)
				{
					$homepath = realpath($path);
					break;
				}
			}
		}
		else
		{
			$ns = 'App';
		}

		// Migrations Config
		$config = new Migrations();

		if ($config->type !== 'timestamp' && $config->type !== 'sequential')
		{
			CLI::error(lang('Migrations.invalidType', [$config->type]));
			return;
		}

		// migration Type
		if ($config->type === 'timestamp')
		{
			$fileName = date('YmdHis_') . $name;
		}
		else if ($config->type === 'sequential')
		{
			// default with 001
			$sequence = $params[0] ?? '001';
			// number must be three digits
			if (! is_numeric($sequence) || strlen($sequence) !== 3)
			{
				CLI::error(lang('Migrations.migNumberError'));
				return;
			}

			$fileName = $sequence . '_' . $name;
		}

		// full path
		$path = $homepath . '/Database/Migrations/' . $fileName . '.php';

		$template = <<<EOD
<?php namespace $ns\Database\Migrations;

use CodeIgniter\Database\Migration;

class Migration_{name} extends Migration
{
	public function up()
	{
		//
	}

	//--------------------------------------------------------------------

	public function down()
	{
		//
	}
}

EOD;
		$template = str_replace('{name}', $name, $template);

		helper('filesystem');
		if (! write_file($path, $template))
		{
			CLI::error(lang('Migrations.writeError'));
			return;
		}

		CLI::write('Created file: ' . CLI::color(str_replace($homepath, $ns, $path), 'green'));
	}

}


<?php

/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\Validation;

use CodeIgniter\HTTP\RequestInterface;
use Config\Services;

/**
 * File validation rules
 */
class FileRules
{

	/**
	 * Request instance. So we can get access to the files.
	 *
	 * @var \CodeIgniter\HTTP\RequestInterface
	 */
	protected $request;

	//--------------------------------------------------------------------

	public function __construct(RequestInterface $request = null)
	{
		if (is_null($request))
		{
			$request = Services::request();
		}

		$this->request = $request;
	}

	//--------------------------------------------------------------------

	/**
	 * Verifies that $name is the name of a valid uploaded file.
	 *
	 * @param string $blank
	 * @param string $name
	 * @param array  $data
	 *
	 * @return boolean
	 */
	public function uploaded(string $blank = null, string $name, array $data): bool
	{
		$file = $this->request->getFile($name);

		if (is_null($file))
		{
			return false;
		}

		if (ENVIRONMENT === 'testing')
		{
			return $file->getError() === 0;
		}

		// Note: cannot unit test this; no way to over-ride ENVIRONMENT?
		// @codeCoverageIgnoreStart
		return $file->isValid();
		// @codeCoverageIgnoreEnd
	}

	//--------------------------------------------------------------------

	/**
	 * Verifies if the file's size in Kilobytes is no larger than the parameter.
	 *
	 * @param string|null $blank
	 * @param string      $params
	 * @param array       $data
	 *
	 * @return boolean
	 */
	public function max_size(string $blank = null, string $params, array $data): bool
	{
		// Grab the file name off the top of the $params
		// after we split it.
		$params = explode(',', $params);
		$name   = array_shift($params);

		$file = $this->request->getFile($name);

		if (is_null($file))
		{
			return false;
		}
		return $params[0] >= $file->getSize() / 1024;
	}

	//--------------------------------------------------------------------

	/**
	 * Uses the mime config file to determine if a file is considered an "image",
	 * which for our purposes basically means that it's a raster image or svg.
	 *
	 * @param string|null $blank
	 * @param string      $params
	 * @param array       $data
	 *
	 * @return boolean
	 */
	public function is_image(string $blank = null, string $params, array $data): bool
	{
		// Grab the file name off the top of the $params
		// after we split it.
		$params = explode(',', $params);
		$name   = array_shift($params);

		$file = $this->request->getFile($name);

		if (is_null($file))
		{
			return false;
		}

		// We know that our mimes list always has the first mime
		// start with `image` even when then are multiple accepted types.
		$type = \Config\Mimes::guessTypeFromExtension($file->getExtension());

		return mb_strpos($type, 'image') === 0;
	}

	//--------------------------------------------------------------------

	/**
	 * Checks to see if an uploaded file's mime type matches one in the parameter.
	 *
	 * @param string|null $blank
	 * @param string      $params
	 * @param array       $data
	 *
	 * @return boolean
	 */
	public function mime_in(string $blank = null, string $params, array $data): bool
	{
		// Grab the file name off the top of the $params
		// after we split it.
		$params = explode(',', $params);
		$name   = array_shift($params);

		$file = $this->request->getFile($name);

		if (is_null($file))
		{
			return false;
		}

		return in_array($file->getMimeType(), $params);
	}

	//--------------------------------------------------------------------

	/**
	 * Checks to see if an uploaded file's extension matches one in the parameter.
	 *
	 * @param string|null $blank
	 * @param string      $params
	 * @param array       $data
	 *
	 * @return boolean
	 */
	public function ext_in(string $blank = null, string $params, array $data): bool
	{
		// Grab the file name off the top of the $params
		// after we split it.
		$params = explode(',', $params);
		$name   = array_shift($params);

		$file = $this->request->getFile($name);

		if (is_null($file))
		{
			return false;
		}

		return in_array($file->getExtension(), $params);
	}

	//--------------------------------------------------------------------

	/**
	 * Checks an uploaded file to verify that the dimensions are within
	 * a specified allowable dimension.
	 *
	 * @param string|null $blank
	 * @param string      $params
	 * @param array       $data
	 *
	 * @return boolean
	 */
	public function max_dims(string $blank = null, string $params, array $data): bool
	{
		// Grab the file name off the top of the $params
		// after we split it.
		$params = explode(',', $params);
		$name   = array_shift($params);

		$file = $this->request->getFile($name);

		if (is_null($file))
		{
			return false;
		}

		// Get Parameter sizes
		$allowedWidth  = $params[0] ?? 0;
		$allowedHeight = $params[1] ?? 0;

		// Get uploaded image size
		$info       = getimagesize($file->getTempName());
		$fileWidth  = $info[0];
		$fileHeight = $info[1];

		return $fileWidth <= $allowedWidth && $fileHeight <= $allowedHeight;
	}

	//--------------------------------------------------------------------
}


<?php

/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 4.0.0
 * @filesource
 */

namespace CodeIgniter\Debug\Toolbar\Collectors;

use CodeIgniter\Config\Services;
use CodeIgniter\View\RendererInterface;

/**
 * Views collector
 */
class Events extends BaseCollector
{

	/**
	 * Whether this collector has data that can
	 * be displayed in the Timeline.
	 *
	 * @var boolean
	 */
	protected $hasTimeline = false;

	/**
	 * Whether this collector needs to display
	 * content in a tab or not.
	 *
	 * @var boolean
	 */
	protected $hasTabContent = true;

	/**
	 * Whether this collector has data that
	 * should be shown in the Vars tab.
	 *
	 * @var boolean
	 */
	protected $hasVarData = false;

	/**
	 * The 'title' of this Collector.
	 * Used to name things in the toolbar HTML.
	 *
	 * @var string
	 */
	protected $title = 'Events';

	/**
	 * Instance of the Renderer service
	 *
	 * @var RendererInterface
	 */
	protected $viewer;

	//--------------------------------------------------------------------

	/**
	 * Constructor.
	 */
	public function __construct()
	{
		$this->viewer = Services::renderer(null, true);
	}

	//--------------------------------------------------------------------

	/**
	 * Child classes should implement this to return the timeline data
	 * formatted for correct usage.
	 *
	 * @return array
	 */
	protected function formatTimelineData(): array
	{
		$data = [];

		$rows = $this->viewer->getPerformanceData();

		foreach ($rows as $name => $info)
		{
			$data[] = [
				'name'      => 'View: ' . $info['view'],
				'component' => 'Views',
				'start'     => $info['start'],
				'duration'  => $info['end'] - $info['start'],
			];
		}

		return $data;
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the data of this collector to be formatted in the toolbar
	 *
	 * @return array
	 */
	public function display(): array
	{
		$data = [
			'events' => [],
		];

		foreach (\CodeIgniter\Events\Events::getPerformanceLogs() as $row)
		{
			$key = $row['event'];

			if (! array_key_exists($key, $data['events']))
			{
				$data['events'][$key] = [
					'event'    => $key,
					'duration' => number_format(($row['end'] - $row['start']) * 1000, 2),
					'count'    => 1,
				];

				continue;
			}

			$data['events'][$key]['duration'] += number_format(($row['end'] - $row['start']) * 1000, 2);
			$data['events'][$key]['count']++;
		}

		return $data;
	}

	//--------------------------------------------------------------------

	/**
	 * Gets the "badge" value for the button.
	 *
	 * @return integer
	 */
	public function getBadgeValue(): int
	{
		return count(\CodeIgniter\Events\Events::getPerformanceLogs());
	}

	//--------------------------------------------------------------------

	/**
	 * Display the icon.
	 *
	 * Icon from https://icons8.com - 1em package
	 *
	 * @return string
	 */
	public function icon(): string
	{
		return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEASURBVEhL7ZXNDcIwDIVTsRBH1uDQDdquUA6IM1xgCA6MwJUN2hk6AQzAz0vl0ETUxC5VT3zSU5w81/mRMGZysixbFEVR0jSKNt8geQU9aRpFmp/keX6AbjZ5oB74vsaN5lSzA4tLSjpBFxsjeSuRy4d2mDdQTWU7YLbXTNN05mKyovj5KL6B7q3hoy3KwdZxBlT+Ipz+jPHrBqOIynZgcZonoukb/0ckiTHqNvDXtXEAaygRbaB9FvUTjRUHsIYS0QaSp+Dw6wT4hiTmYHOcYZsdLQ2CbXa4ftuuYR4x9vYZgdb4vsFYUdmABMYeukK9/SUme3KMFQ77+Yfzh8eYF8+orDuDWU5LAAAAAElFTkSuQmCC';
	}
}


<?php

/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\HTTP;

/**
 * Representation of an HTTP request.
 */
class Request extends Message implements RequestInterface
{

	/**
	 * IP address of the current user.
	 *
	 * @var string
	 */
	protected $ipAddress = '';

	/**
	 * Proxy IPs
	 *
	 * @var string|array
	 */
	protected $proxyIPs;

	/**
	 * Request method.
	 *
	 * @var string
	 */
	protected $method;

	/**
	 * Stores values we've retrieved from
	 * PHP globals.
	 *
	 * @var array
	 */
	protected $globals = [];

	//--------------------------------------------------------------------

	/**
	 * Constructor.
	 *
	 * @param object $config
	 */
	public function __construct($config)
	{
		$this->proxyIPs = $config->proxyIPs;

		$this->method = $this->getServer('REQUEST_METHOD') ?? 'GET';
	}

	//--------------------------------------------------------------------

	/**
	 * Gets the user's IP address.
	 *
	 * @return string IP address
	 */
	public function getIPAddress(): string
	{
		if (! empty($this->ipAddress))
		{
			return $this->ipAddress;
		}

		$proxy_ips = $this->proxyIPs;
		if (! empty($this->proxyIPs) && ! is_array($this->proxyIPs))
		{
			$proxy_ips = explode(',', str_replace(' ', '', $this->proxyIPs));
		}

		$this->ipAddress = $this->getServer('REMOTE_ADDR');

		if ($proxy_ips)
		{
			foreach (['HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP'] as $header)
			{
				if (($spoof = $this->getServer($header)) !== null)
				{
					// Some proxies typically list the whole chain of IP
					// addresses through which the client has reached us.
					// e.g. client_ip, proxy_ip1, proxy_ip2, etc.
					sscanf($spoof, '%[^,]', $spoof);

					if (! $this->isValidIP($spoof))
					{
						$spoof = null;
					}
					else
					{
						break;
					}
				}
			}

			if ($spoof)
			{
				for ($i = 0, $c = count($proxy_ips); $i < $c; $i ++)
				{
					// Check if we have an IP address or a subnet
					if (strpos($proxy_ips[$i], '/') === false)
					{
						// An IP address (and not a subnet) is specified.
						// We can compare right away.
						if ($proxy_ips[$i] === $this->ipAddress)
						{
							$this->ipAddress = $spoof;
							break;
						}

						continue;
					}

					// We have a subnet ... now the heavy lifting begins
					isset($separator) || $separator = $this->isValidIP($this->ipAddress, 'ipv6') ? ':' : '.';

					// If the proxy entry doesn't match the IP protocol - skip it
					if (strpos($proxy_ips[$i], $separator) === false)
					{
						continue;
					}

					// Convert the REMOTE_ADDR IP address to binary, if needed
					if (! isset($ip, $sprintf))
					{
						if ($separator === ':')
						{
							// Make sure we're have the "full" IPv6 format
							$ip = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($this->ipAddress, ':')), $this->ipAddress
									)
							);

							for ($j = 0; $j < 8; $j ++)
							{
								$ip[$j] = intval($ip[$j], 16);
							}

							$sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b';
						}
						else
						{
							$ip      = explode('.', $this->ipAddress);
							$sprintf = '%08b%08b%08b%08b';
						}

						$ip = vsprintf($sprintf, $ip);
					}

					// Split the netmask length off the network address
					sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen);

					// Again, an IPv6 address is most likely in a compressed form
					if ($separator === ':')
					{
						$netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr));
						for ($i = 0; $i < 8; $i ++)
						{
							$netaddr[$i] = intval($netaddr[$i], 16);
						}
					}
					else
					{
						$netaddr = explode('.', $netaddr);
					}

					// Convert to binary and finally compare
					if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0)
					{
						$this->ipAddress = $spoof;
						break;
					}
				}
			}
		}

		if (! $this->isValidIP($this->ipAddress))
		{
			return $this->ipAddress = '0.0.0.0';
		}

		return empty($this->ipAddress) ? '' : $this->ipAddress;
	}

	//--------------------------------------------------------------------

	/**
	 * Validate an IP address
	 *
	 * @param string $ip    IP Address
	 * @param string $which IP protocol: 'ipv4' or 'ipv6'
	 *
	 * @return boolean
	 */
	public function isValidIP(string $ip = null, string $which = null): bool
	{
		switch (strtolower($which))
		{
			case 'ipv4':
				$which = FILTER_FLAG_IPV4;
				break;
			case 'ipv6':
				$which = FILTER_FLAG_IPV6;
				break;
			default:
				$which = null;
				break;
		}

		return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which);
	}

	//--------------------------------------------------------------------

	/**
	 * Get the request method.
	 *
	 * @param boolean $upper Whether to return in upper or lower case.
	 *
	 * @return string
	 */
	public function getMethod(bool $upper = false): string
	{
		return ($upper) ? strtoupper($this->method) : strtolower($this->method);
	}

	//--------------------------------------------------------------------

	/**
	 * Sets the request method. Used when spoofing the request.
	 *
	 * @param string $method
	 *
	 * @return Request
	 */
	public function setMethod(string $method)
	{
		$this->method = $method;

		return $this;
	}

	//--------------------------------------------------------------------

	/**
	 * Fetch an item from the $_SERVER array.
	 *
	 * @param string|array|null $index  Index for item to be fetched from $_SERVER
	 * @param integer|null      $filter A filter name to be applied
	 * @param null              $flags
	 *
	 * @return mixed
	 */
	public function getServer($index = null, $filter = null, $flags = null)
	{
		return $this->fetchGlobal('server', $index, $filter, $flags);
	}

	//--------------------------------------------------------------------

	/**
	 * Fetch an item from the $_ENV array.
	 *
	 * @param null $index  Index for item to be fetched from $_ENV
	 * @param null $filter A filter name to be applied
	 * @param null $flags
	 *
	 * @return mixed
	 */
	public function getEnv($index = null, $filter = null, $flags = null)
	{
		return $this->fetchGlobal('env', $index, $filter, $flags);
	}

	//--------------------------------------------------------------------

	/**
	 * Allows manually setting the value of PHP global, like $_GET, $_POST, etc.
	 *
	 * @param string $method
	 * @param $value
	 *
	 * @return $this
	 */
	public function setGlobal(string $method, $value)
	{
		$this->globals[$method] = $value;

		return $this;
	}

	//--------------------------------------------------------------------

	/**
	 * Fetches one or more items from a global, like cookies, get, post, etc.
	 * Can optionally filter the input when you retrieve it by passing in
	 * a filter.
	 *
	 * If $type is an array, it must conform to the input allowed by the
	 * filter_input_array method.
	 *
	 * http://php.net/manual/en/filter.filters.sanitize.php
	 *
	 * @param string            $method Input filter constant
	 * @param string|array|null $index
	 * @param integer|null      $filter Filter constant
	 * @param mixed             $flags
	 *
	 * @return mixed
	 */
	public function fetchGlobal($method, $index = null, $filter = null, $flags = null)
	{
		$method = strtolower($method);

		if (! isset($this->globals[$method]))
		{
			$this->populateGlobals($method);
		}

		// Null filters cause null values to return.
		if (is_null($filter))
		{
			$filter = FILTER_DEFAULT;
		}

		// Return all values when $index is null
		if (is_null($index))
		{
			$values = [];
			foreach ($this->globals[$method] as $key => $value)
			{
				$values[$key] = is_array($value)
					? $this->fetchGlobal($method, $key, $filter, $flags)
					: filter_var($value, $filter, $flags);
			}

			return $values;
		}

		// allow fetching multiple keys at once
		if (is_array($index))
		{
			$output = [];

			foreach ($index as $key)
			{
				$output[$key] = $this->fetchGlobal($method, $key, $filter, $flags);
			}

			return $output;
		}

		// Does the index contain array notation?
		if (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1)
		{
			$value = $this->globals[$method];
			for ($i = 0; $i < $count; $i++)
			{
				$key = trim($matches[0][$i], '[]');

				if ($key === '') // Empty notation will return the value as array
				{
					break;
				}

				if (isset($value[$key]))
				{
					$value = $value[$key];
				}
				else
				{
					return null;
				}
			}
		}

		if (! isset($value))
		{
			$value = $this->globals[$method][$index] ?? null;
		}

		// Cannot filter these types of data automatically...
		if (is_array($value) || is_object($value) || is_null($value))
		{
			return $value;
		}

		return filter_var($value, $filter, $flags);
	}

	//--------------------------------------------------------------------

	/**
	 * Saves a copy of the current state of one of several PHP globals
	 * so we can retrieve them later.
	 *
	 * @param string $method
	 */
	protected function populateGlobals(string $method)
	{
		if (! isset($this->globals[$method]))
		{
			$this->globals[$method] = [];
		}

		// Don't populate ENV as it might contain
		// sensitive data that we don't want to get logged.
		switch($method)
		{
			case 'get':
				$this->globals['get'] = $_GET;
				break;
			case 'post':
				$this->globals['post'] = $_POST;
				break;
			case 'request':
				$this->globals['request'] = $_REQUEST;
				break;
			case 'cookie':
				$this->globals['cookie'] = $_COOKIE;
				break;
			case 'server':
				$this->globals['server'] = $_SERVER;
				break;
		}
	}
}


<?php

/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\Commands\Database;

use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use Config\Services;

/**
 * Migrates the DB to a specific version.
 *
 * @package CodeIgniter\Commands
 */
class MigrateVersion extends BaseCommand
{

	/**
	 * The group the command is lumped under
	 * when listing commands.
	 *
	 * @var string
	 */
	protected $group = 'Database';

	/**
	 * The Command's name
	 *
	 * @var string
	 */
	protected $name = 'migrate:version';

	/**
	 * the Command's short description
	 *
	 * @var string
	 */
	protected $description = 'Migrates the database up or down to get to the specified version.';

	/**
	 * the Command's usage
	 *
	 * @var string
	 */
	protected $usage = 'migrate:version [version_number] [Options]';

	/**
	 * the Command's Arguments
	 *
	 * @var array
	 */
	protected $arguments = [
		'version_number' => 'The version number to migrate',
	];

	/**
	 * the Command's Options
	 *
	 * @var array
	 */
	protected $options = [
		'-n' => 'Set migration namespace',
		'-g' => 'Set database group',
	];

	/**
	 * Migrates the database up or down to get to the specified version.
	 *
	 * @param array $params
	 */
	public function run(array $params = [])
	{
		$runner = Services::migrations();

		// Get the version number
		$version = array_shift($params);

		if (is_null($version))
		{
			$version = CLI::prompt(lang('Migrations.version'));
		}

		if (is_null($version))
		{
			CLI::error(lang('Migrations.invalidVersion'));
			exit();
		}

		CLI::write(sprintf(lang('Migrations.toVersionPH'), $version), 'yellow');

		$namespace = $params['-n'] ?? CLI::getOption('n');
		$group     = $params['-g'] ?? CLI::getOption('g');

		try
		{
			$runner->version($version, $namespace, $group);
			CLI::write('Done');
		}
		catch (\Exception $e)
		{
			$this->showError($e);
		}
	}

}


<?php
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\HTTP;

use Config\App;

/**
 * Class CLIRequest
 *
 * Represents a request from the command-line. Provides additional
 * tools to interact with that request since CLI requests are not
 * static like HTTP requests might be.
 *
 * Portions of this code were initially from the FuelPHP Framework,
 * version 1.7.x, and used here under the MIT license they were
 * originally made available under.
 *
 * http://fuelphp.com
 *
 * @package CodeIgniter\HTTP
 */
class CLIRequest extends Request
{

	/**
	 * Stores the segments of our cli "URI" command.
	 *
	 * @var array
	 */
	protected $segments = [];

	/**
	 * Command line options and their values.
	 *
	 * @var array
	 */
	protected $options = [];

	//--------------------------------------------------------------------

	/**
	 * Constructor
	 *
	 * @param App $config
	 */
	public function __construct(App $config)
	{
		parent::__construct($config);

		// Don't terminate the script when the cli's tty goes away
		ignore_user_abort(true);

		$this->parseCommand();
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the "path" of the request script so that it can be used
	 * in routing to the appropriate controller/method.
	 *
	 * The path is determined by treating the command line arguments
	 * as if it were a URL - up until we hit our first option.
	 *
	 * Example:
	 *      php index.php users 21 profile -foo bar
	 *
	 *      // Routes to /users/21/profile (index is removed for routing sake)
	 *      // with the option foo = bar.
	 *
	 * @return string
	 */
	public function getPath(): string
	{
		$path = implode('/', $this->segments);

		return empty($path) ? '' : $path;
	}

	//--------------------------------------------------------------------

	/**
	 * Returns an associative array of all CLI options found, with
	 * their values.
	 *
	 * @return array
	 */
	public function getOptions(): array
	{
		return $this->options;
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the path segments.
	 *
	 * @return array
	 */
	public function getSegments(): array
	{
		return $this->segments;
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the value for a single CLI option that was passed in.
	 *
	 * @param string $key
	 *
	 * @return string|null
	 */
	public function getOption(string $key)
	{
		return $this->options[$key] ?? null;
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the options as a string, suitable for passing along on
	 * the CLI to other commands.
	 *
	 * Example:
	 *      $options = [
	 *          'foo' => 'bar',
	 *          'baz' => 'queue some stuff'
	 *      ];
	 *
	 *      getOptionString() = '-foo bar -baz "queue some stuff"'
	 *
	 * @return string
	 */
	public function getOptionString(): string
	{
		if (empty($this->options))
		{
			return '';
		}

		$out = '';

		foreach ($this->options as $name => $value)
		{
			// If there's a space, we need to group
			// so it will pass correctly.
			if (strpos($value, ' ') !== false)
			{
				$value = '"' . $value . '"';
			}

			$out .= "-{$name} $value ";
		}

		return trim($out);
	}

	//--------------------------------------------------------------------

	/**
	 * Parses the command line it was called from and collects all options
	 * and valid segments.
	 *
	 * NOTE: I tried to use getopt but had it fail occasionally to find
	 * any options, where argv has always had our back.
	 */
	protected function parseCommand()
	{
		// Since we're building the options ourselves,
		// we stop adding it to the segments array once
		// we have found the first dash.
		$options_found = false;

		$argc = $this->getServer('argc', FILTER_SANITIZE_NUMBER_INT);
		$argv = $this->getServer('argv');

		// We start at 1 since we never want to include index.php
		for ($i = 1; $i < $argc; $i ++)
		{
			// If there's no '-' at the beginning of the argument
			// then add it to our segments.
			if (! $options_found && strpos($argv[$i], '-') === false)
			{
				$this->segments[] = filter_var($argv[$i], FILTER_SANITIZE_STRING);
				continue;
			}

			$options_found = true;

			if (strpos($argv[$i], '-') !== 0)
			{
				continue;
			}

			$arg   = filter_var(str_replace('-', '', $argv[$i]), FILTER_SANITIZE_STRING);
			$value = null;

			// If the next item starts with a dash it's a value
			if (isset($argv[$i + 1]) && strpos($argv[$i + 1], '-') !== 0)
			{
				$value = filter_var($argv[$i + 1], FILTER_SANITIZE_STRING);
				$i ++;
			}

			$this->options[$arg] = $value;
		}
	}

	//--------------------------------------------------------------------
}


<?php
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\Database\SQLite3;

use CodeIgniter\Database\Exceptions\DataException;

/**
 * Class Table
 *
 * Provides missing features for altering tables that are common
 * in other supported databases, but are missing from SQLite.
 * These are needed in order to support migrations during testing
 * when another database is used as the primary engine, but
 * SQLite in memory databases are used for faster test execution.
 *
 * @package CodeIgniter\Database\SQLite3
 */
class Table
{
	/**
	 * All of the fields this table represents.
	 *
	 * @var array
	 */
	protected $fields = [];

	/**
	 * All of the unique/primary keys in the table.
	 *
	 * @var array
	 */
	protected $keys = [];

	/**
	 * All of the foreign keys in the table.
	 *
	 * @var array
	 */
	protected $foreignKeys = [];

	/**
	 * The name of the table we're working with.
	 *
	 * @var string
	 */
	protected $tableName;

	/**
	 * The name of the table, with database prefix
	 *
	 * @var string
	 */
	protected $prefixedTableName;

	/**
	 * @var Connection
	 */
	protected $db;

	/**
	 * @var Forge
	 */
	protected $forge;

	/**
	 * Table constructor.
	 *
	 * @param Connection $db
	 */
	public function __construct(Connection $db, Forge $forge)
	{
		$this->db    = $db;
		$this->forge = $forge;
	}

	/**
	 * Reads an existing database table and
	 * collects all of the information needed to
	 * recreate this table.
	 *
	 * @param string $table
	 *
	 * @return \CodeIgniter\Database\SQLite3\Table
	 */
	public function fromTable(string $table)
	{
		$this->prefixedTableName = $table;

		// Remove the prefix, if any, since it's
		// already been added by the time we get here...
		$prefix = $this->db->DBPrefix;
		if (! empty($prefix))
		{
			if (strpos($table, $prefix) === 0)
			{
				$table = substr($table, strlen($prefix));
			}
		}

		if (! $this->db->tableExists($this->prefixedTableName))
		{
			throw DataException::forTableNotFound($this->prefixedTableName);
		}

		$this->tableName = $table;

		$this->fields = $this->formatFields($this->db->getFieldData($table));

		$this->keys = array_merge($this->keys, $this->formatKeys($this->db->getIndexData($table)));

		$this->foreignKeys = $this->db->getForeignKeyData($table);

		return $this;
	}

	/**
	 * Called after `fromTable` and any actions, like `dropColumn`, etc,
	 * to finalize the action. It creates a temp table, creates the new
	 * table with modifications, and copies the data over to the new table.
	 *
	 * @return boolean
	 */
	public function run(): bool
	{
		$this->db->query('PRAGMA foreign_keys = OFF');

		$this->db->transStart();

		$this->forge->renameTable($this->tableName, "temp_{$this->tableName}");

		$this->forge->reset();

		$this->createTable();

		$this->copyData();

		$this->forge->dropTable("temp_{$this->tableName}");

		$success = $this->db->transComplete();

		$this->db->query('PRAGMA foreign_keys = ON');

		return $success;
	}

	/**
	 * Drops a column from the table.
	 *
	 * @param string $column
	 *
	 * @return \CodeIgniter\Database\SQLite3\Table
	 */
	public function dropColumn(string $column)
	{
		unset($this->fields[$column]);

		return $this;
	}

	/**
	 * Modifies a field, including changing data type,
	 * renaming, etc.
	 *
	 * @param array $field
	 *
	 * @return \CodeIgniter\Database\SQLite3\Table
	 */
	public function modifyColumn(array $field)
	{
		$field = $field[0];

		$oldName = $field['name'];
		unset($field['name']);

		$this->fields[$oldName] = $field;

		return $this;
	}

	/**
	 * Creates the new table based on our current fields.
	 *
	 * @return mixed
	 */
	protected function createTable()
	{
		$this->dropIndexes();
		$this->db->resetDataCache();

		// Handle any modified columns.
		$fields = [];
		foreach ($this->fields as $name => $field)
		{
			if (isset($field['new_name']))
			{
				$fields[$field['new_name']] = $field;
				continue;
			}

			$fields[$name] = $field;
		}

		$this->forge->addField($fields);

		// Unique/Index keys
		if (is_array($this->keys))
		{
			foreach ($this->keys as $key)
			{
				switch ($key['type'])
				{
					case 'primary':
						$this->forge->addPrimaryKey($key['fields']);
						break;
					case 'unique':
						$this->forge->addUniqueKey($key['fields']);
						break;
					case 'index':
						$this->forge->addKey($key['fields']);
						break;
				}
			}
		}

		// Foreign Keys

		return $this->forge->createTable($this->tableName);
	}

	/**
	 * Copies data from our old table to the new one,
	 * taking care map data correctly based on any columns
	 * that have been renamed.
	 *
	 * @return void
	 */
	protected function copyData()
	{
		$exFields  = [];
		$newFields = [];

		foreach ($this->fields as $name => $details)
		{
			// Are we modifying the column?
			if (isset($details['new_name']))
			{
				$newFields[] = $details['new_name'];
			}
			else
			{
				$newFields[] = $name;
			}

			$exFields[] = $name;
		}

		$exFields  = implode(', ', $exFields);
		$newFields = implode(', ', $newFields);

		$this->db->query("INSERT INTO {$this->prefixedTableName}({$newFields}) SELECT {$exFields} FROM {$this->db->DBPrefix}temp_{$this->tableName}");
	}

	/**
	 * Converts fields retrieved from the database to
	 * the format needed for creating fields with Forge.
	 *
	 * @param array|boolean $fields
	 *
	 * @return mixed
	 */
	protected function formatFields($fields)
	{
		if (! is_array($fields))
		{
			return $fields;
		}

		$return = [];

		foreach ($fields as $field)
		{
			$return[$field->name] = [
				'type'     => $field->type,
				'default'  => $field->default,
				'nullable' => $field->nullable,
			];

			if ($field->primary_key)
			{
				$this->keys[$field->name] = [
					'fields' => [$field->name],
					'type'   => 'primary',
				];
			}
		}

		return $return;
	}

	/**
	 * Converts keys retrieved from the database to
	 * the format needed to create later.
	 *
	 * @param mixed $keys
	 *
	 * @return mixed
	 */
	protected function formatKeys($keys)
	{
		if (! is_array($keys))
		{
			return $keys;
		}

		$return = [];

		foreach ($keys as $name => $key)
		{
			$return[$name] = [
				'fields' => $key->fields,
				'type'   => 'index',
			];
		}

		return $return;
	}

	/**
	 * Attempts to drop all indexes and constraints
	 * from the database for this table.
	 *
	 * @return null|void
	 */
	protected function dropIndexes()
	{
		if (! is_array($this->keys) || ! count($this->keys))
		{
			return;
		}

		foreach ($this->keys as $name => $key)
		{
			if ($key['type'] === 'primary' || $key['type'] === 'unique')
			{
				continue;
			}

			$this->db->query("DROP INDEX IF EXISTS '{$name}'");
		}
	}
}


<?php

/**
 * CodeIgniter
 *
 * An open source application development framework for PHP
 *
 * This content is released under the MIT License (MIT)
 *
 * Copyright (c) 2014-2019 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     CodeIgniter Dev Team
 * @copyright  2014-2019 British Columbia Institute of Technology (https://bcit.ca/)
 * @license    https://opensource.org/licenses/MIT	MIT License
 * @link       https://codeigniter.com
 * @since      Version 3.0.0
 * @filesource
 */

namespace CodeIgniter\Security;

use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\Security\Exceptions\SecurityException;

/**
 * HTTP security handler.
 */
class Security
{

	/**
	 * CSRF Hash
	 *
	 * Random hash for Cross Site Request Forgery protection cookie
	 *
	 * @var string
	 */
	protected $CSRFHash;

	/**
	 * CSRF Expire time
	 *
	 * Expiration time for Cross Site Request Forgery protection cookie.
	 * Defaults to two hours (in seconds).
	 *
	 * @var integer
	 */
	protected $CSRFExpire = 7200;

	/**
	 * CSRF Token name
	 *
	 * Token name for Cross Site Request Forgery protection cookie.
	 *
	 * @var string
	 */
	protected $CSRFTokenName = 'CSRFToken';

	/**
	 * CSRF Cookie name
	 *
	 * Cookie name for Cross Site Request Forgery protection cookie.
	 *
	 * @var string
	 */
	protected $CSRFCookieName = 'CSRFToken';

	/**
	 * CSRF Regenerate
	 *
	 * If true, the CSRF Token will be regenerated on every request.
	 * If false, will stay the same for the life of the cookie.
	 *
	 * @var boolean
	 */
	protected $CSRFRegenerate = true;

	/**
	 * Typically will be a forward slash
	 *
	 * @var string
	 */
	protected $cookiePath = '/';

	/**
	 * Set to .your-domain.com for site-wide cookies
	 *
	 * @var string
	 */
	protected $cookieDomain = '';

	/**
	 * Cookie will only be set if a secure HTTPS connection exists.
	 *
	 * @var boolean
	 */
	protected $cookieSecure = false;

	/**
	 * List of sanitize filename strings
	 *
	 * @var array
	 */
	public $filenameBadChars = [
		'../',
		'<!--',
		'-->',
		'<',
		'>',
		"'",
		'"',
		'&',
		'$',
		'#',
		'{',
		'}',
		'[',
		']',
		'=',
		';',
		'?',
		'%20',
		'%22',
		'%3c', // <
		'%253c', // <
		'%3e', // >
		'%0e', // >
		'%28', // (
		'%29', // )
		'%2528', // (
		'%26', // &
		'%24', // $
		'%3f', // ?
		'%3b', // ;
		'%3d',       // =
	];

	//--------------------------------------------------------------------

	/**
	 * Security constructor.
	 *
	 * Stores our configuration and fires off the init() method to
	 * setup initial state.
	 *
	 * @param \Config\App $config
	 *
	 * @throws \Exception
	 */
	public function __construct($config)
	{
		// Store our CSRF-related settings
		$this->CSRFExpire     = $config->CSRFExpire;
		$this->CSRFTokenName  = $config->CSRFTokenName;
		$this->CSRFCookieName = $config->CSRFCookieName;
		$this->CSRFRegenerate = $config->CSRFRegenerate;

		if (isset($config->cookiePrefix))
		{
			$this->CSRFCookieName = $config->cookiePrefix . $this->CSRFCookieName;
		}

		// Store cookie-related settings
		$this->cookiePath   = $config->cookiePath;
		$this->cookieDomain = $config->cookieDomain;
		$this->cookieSecure = $config->cookieSecure;

		$this->CSRFSetHash();

		unset($config);
	}

	//--------------------------------------------------------------------

	/**
	 * CSRF Verify
	 *
	 * @param RequestInterface $request
	 *
	 * @return $this|false
	 * @throws \Exception
	 */
	public function CSRFVerify(RequestInterface $request)
	{
		// If it's not a POST request we will set the CSRF cookie
		if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
		{
			return $this->CSRFSetCookie($request);
		}

		// Do the tokens exist in both the _POST and _COOKIE arrays?
		if (! isset($_POST[$this->CSRFTokenName], $_COOKIE[$this->CSRFCookieName]) || $_POST[$this->CSRFTokenName] !== $_COOKIE[$this->CSRFCookieName]
		) // Do the tokens match?
		{
			throw SecurityException::forDisallowedAction();
		}

		// We kill this since we're done and we don't want to pollute the _POST array
		unset($_POST[$this->CSRFTokenName]);

		// Regenerate on every submission?
		if ($this->CSRFRegenerate)
		{
			// Nothing should last forever
			unset($_COOKIE[$this->CSRFCookieName]);
		}

		$this->CSRFSetHash();
		$this->CSRFSetCookie($request);

		log_message('info', 'CSRF token verified');

		return $this;
	}

	//--------------------------------------------------------------------

	/**
	 * CSRF Set Cookie
	 *
	 * @codeCoverageIgnore
	 *
	 * @param RequestInterface|\CodeIgniter\HTTP\IncomingRequest $request
	 *
	 * @return Security|false
	 */
	public function CSRFSetCookie(RequestInterface $request)
	{
		$expire        = time() + $this->CSRFExpire;
		$secure_cookie = (bool) $this->cookieSecure;

		if ($secure_cookie && ! $request->isSecure())
		{
			return false;
		}

		setcookie(
				$this->CSRFCookieName, $this->CSRFHash, $expire, $this->cookiePath, $this->cookieDomain, $secure_cookie, true                // Enforce HTTP only cookie for security
		);

		log_message('info', 'CSRF cookie sent');

		return $this;
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the current CSRF Hash.
	 *
	 * @return string
	 */
	public function getCSRFHash(): string
	{
		return $this->CSRFHash;
	}

	//--------------------------------------------------------------------

	/**
	 * Returns the CSRF Token Name.
	 *
	 * @return string
	 */
	public function getCSRFTokenName(): string
	{
		return $this->CSRFTokenName;
	}

	//--------------------------------------------------------------------

	/**
	 * Sets the CSRF Hash and cookie.
	 *
	 * @return string
	 * @throws \Exception
	 */
	protected function CSRFSetHash(): string
	{
		if ($this->CSRFHash === null)
		{
			// If the cookie exists we will use its value.
			// We don't necessarily want to regenerate it with
			// each page load since a page could contain embedded
			// sub-pages causing this feature to fail
			if (isset($_COOKIE[$this->CSRFCookieName]) && is_string($_COOKIE[$this->CSRFCookieName]) && preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->CSRFCookieName]) === 1
			)
			{
				return $this->CSRFHash = $_COOKIE[$this->CSRFCookieName];
			}

			$rand           = random_bytes(16);
			$this->CSRFHash = bin2hex($rand);
		}

		return $this->CSRFHash;
	}

	//--------------------------------------------------------------------

	/**
	 * Sanitize Filename
	 *
	 * Tries to sanitize filenames in order to prevent directory traversal attempts
	 * and other security threats, which is particularly useful for files that
	 * were supplied via user input.
	 *
	 * If it is acceptable for the user input to include relative paths,
	 * e.g. file/in/some/approved/folder.txt, you can set the second optional
	 * parameter, $relative_path to TRUE.
	 *
	 * @param string  $str           Input file name
	 * @param boolean $relative_path Whether to preserve paths
	 *
	 * @return string
	 */
	public function sanitizeFilename(string $str, bool $relative_path = false): string
	{
		$bad = $this->filenameBadChars;

		if (! $relative_path)
		{
			$bad[] = './';
			$bad[] = '/';
		}

		$str = remove_invisible_characters($str, false);

		do
		{
			$old = $str;
			$str = str_replace($bad, '', $str);
		}
		while ($old !== $str);

		return stripslashes($str);
	}

	//--------------------------------------------------------------------
}