<?php

/**

 * The SlideshowPluginSecurity class contains functions for sanitizing in- and output.

 *

 * @since 2.1.16

 * @author Stefan Boonstra

 */

class SlideshowPluginSecurity

{

	/**

	 * @since 2.1.16

	 * @var array List of allowed element tags

	 */

	private static $allowedElements = array(

		'b'      => array('endTag' => true, 'attributes' => 'default'),

		'br'     => array('endTag' => false),

		'div'    => array('endTag' => true, 'attributes' => 'default'),

		'h1'     => array('endTag' => true, 'attributes' => 'default'),

		'h2'     => array('endTag' => true, 'attributes' => 'default'),

		'h3'     => array('endTag' => true, 'attributes' => 'default'),

		'h4'     => array('endTag' => true, 'attributes' => 'default'),

		'h5'     => array('endTag' => true, 'attributes' => 'default'),

		'h6'     => array('endTag' => true, 'attributes' => 'default'),

		'i'      => array('endTag' => true, 'attributes' => 'default'),

		'li'     => array('endTag' => true, 'attributes' => 'default'),

		'ol'     => array('endTag' => true, 'attributes' => 'default'),

		'p'      => array('endTag' => true, 'attributes' => 'default'),

		'span'   => array('endTag' => true, 'attributes' => 'default'),

		'strong' => array('endTag' => true, 'attributes' => 'default'),

		'sub'    => array('endTag' => true, 'attributes' => 'default'),

		'sup'    => array('endTag' => true, 'attributes' => 'default'),

		'table'  => array('endTag' => true, 'attributes' => 'default'),

		'tbody'  => array('endTag' => true, 'attributes' => 'default'),

		'td'     => array('endTag' => true, 'attributes' => 'default'),

		'tfoot'  => array('endTag' => true, 'attributes' => 'default'),

		'th'     => array('endTag' => true, 'attributes' => 'default'),

		'thead'  => array('endTag' => true, 'attributes' => 'default'),

		'tr'     => array('endTag' => true, 'attributes' => 'default'),

		'ul'     => array('endTag' => true, 'attributes' => 'default')

	);



	/**

	 * @since 2.1.16

	 * @var array List of attributes allowed in the tags

	 */

	private static $defaultAllowedAttributes = array(

		'class',

		'id',

		'style'

	);



	/**

	 * Similar to the htmlspecialchars($text) function, except this function

	 * allows the exceptions defined in this class.

	 *

	 * @since 2.1.16

	 */

	static function htmlspecialchars_allow_exceptions($text)

	{

		$text = htmlspecialchars(htmlspecialchars_decode($text));



		$allowedElements = self::$allowedElements;



		// Loop through allowed elements decoding their HTML special chars and allowed attributes.

		if (is_array($allowedElements) &&

			count($allowedElements) > 0)

		{

			foreach ($allowedElements as $element => $attributes)

			{

				$position = 0;



				while (($position = stripos($text, $element, $position)) !== false) // While element tags found

				{

					$openingTag        = '<';

					$encodedOpeningTag = htmlspecialchars($openingTag);



					if (substr($text, $position - strlen($encodedOpeningTag), strlen($encodedOpeningTag)) == $encodedOpeningTag) // Check if an opening tag '<' can be found before the tag name

					{

						// Replace encoded opening tag

						$text      = substr_replace($text, '<', $position - strlen($encodedOpeningTag), strlen($encodedOpeningTag));

						$position -= strlen($encodedOpeningTag) - strlen($openingTag);



						// Get the position of the first element closing tag

						$closingTag         = '>';

						$encodedClosingTag  = htmlspecialchars($closingTag);

						$closingTagPosition = stripos($text, $encodedClosingTag, $position);



						// Replace encoded closing tag

						if ($closingTagPosition !== false)

						{

							$text = substr_replace($text, '>', $closingTagPosition, strlen($encodedClosingTag));

						}



						$elementAttributes = null;



						if (isset($attributes['attributes']) && is_array($attributes['attributes']))

						{

							$elementAttributes = $attributes['attributes'];

						}

						elseif (isset($attributes['attributes']) && $attributes['attributes'] == 'default')

						{

							$elementAttributes = self::$defaultAllowedAttributes;

						}

						else

						{

							continue;

						}



						if (!is_array($elementAttributes))

						{

							continue;

						}



						$tagText = substr($text, $position, $closingTagPosition - $position);



						// Decode allowed attributes

						foreach ($elementAttributes as $attribute)

						{

							$attributeOpener = $attribute . '=' . htmlspecialchars('"');



							$attributePosition = 0;



							if (($attributePosition = stripos($tagText, $attributeOpener, $attributePosition)) !== false) // Attribute was found

							{

								$attributeClosingPosition = 0;



								if (($attributeClosingPosition = stripos($tagText, htmlspecialchars('"'), $attributePosition + strlen($attributeOpener))) === false) // If no closing position of attribute was found, skip.

								{

									continue;

								}



								// Open the attribute

								$tagText = str_ireplace($attributeOpener, $attribute . '="', $tagText);



								// Close the attribute

								$attributeClosingPosition -= strlen($attributeOpener) - strlen($attribute . '="');

								$tagText                   = substr_replace($tagText, '"', $attributeClosingPosition, strlen(htmlspecialchars('"')));

							}



						}



						// Put the attributes of the tag back in place

						$text = substr_replace($text, $tagText, $position, $closingTagPosition - $position);

					}



					$position++;

				}



				// Decode closing tags

				if (isset($attributes['endTag']) && $attributes['endTag'])

				{

					$text = str_ireplace(htmlspecialchars('</' . $element . '>'), '</' . $element . '>', $text);

				}

			}

		}



		return $text;

	}

}