<?php
/***************************************************************
 * Copyright notice
 *
 * (c) 2012 Franz G. Jahn <franzjahn@cron-it.de>
 * (c) 2012 Helmut Hummel <helmut.hummel@typo3.org>
 * All rights reserved
 *
 * This script is part of the TYPO3 project. The TYPO3 project is
 * free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * The GNU General Public License can be found at
 * http://www.gnu.org/copyleft/gpl.html.
 *
 * This script is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/

/**
 * Adopted from OWASP Enterprise Security API (ESAPI) reference implementation for the JavaScript Codec.
 * Original Author: Mike Boberski
 *
 * This class provides encoding for user input that is intended to be used in a JavaScript context.
 * It encodes all characters except alphanumericals and the immune characters to a hex representation.
 *
 * @package TYPO3
 * @subpackage t3lib
 *
 * @author Mike Boberski <boberski_michael@bah.com>
 * @copyright 2009-2010 The OWASP Foundation
 * @link http://www.owasp.org/index.php/ESAPI
 *
 * @author Franz G. Jahn <franzjahn@cron-it.de>
 * @author Helmut Hummel <helmut.hummel@typo3.org>
 */
class t3lib_codec_JavaScriptEncoder implements t3lib_Singleton {
	/**
	 * A map where the keys are ordinal values of non-alphanumeric single-byte
	 * characters and the values are hexadecimal equivalents as strings.
	 *
	 * @var array
	 */
	protected $hexMatrix = array();

	/**
	 * Characters that are immune (not dangerous) in the JavaScript context
	 *
	 * @var array
	 */
	protected $immuneCharacters = array(',', '.', '_' );

	/**
	 * TYPO3 charset encoding object
	 *
	 * @var t3lib_cs
	 */
	protected $charsetConversion = NULL;

	/**
	 * Populates the $hex map of non-alphanumeric single-byte characters.
	 *
	 * Alphanumerical character are set to NULL in the matrix.
	 */
	public function __construct() {
		$this->charsetConversion = t3lib_div::makeInstance('t3lib_cs');

		for ($i = 0; $i < 256; $i++) {
			if (($i >= ord('0') && $i <= ord('9')) || ($i >= ord('A') && $i <= ord('Z')) || ($i >= ord('a') && $i <= ord('z'))) {
				$this->hexMatrix[$i] = NULL;
			} else {
				$this->hexMatrix[$i] = dechex($i);
			}
		}
	}

	/**
	 * Encodes a string for JavaScript.
	 *
	 * @param string $input The string to encode, may be empty.
	 * @return string The encoded string.
	 */
	public function encode($input) {
		$stringLength = $this->charsetConversion->strlen('utf-8', $input);
		$encodedString = '';
		for ($i = 0; $i < $stringLength; $i++) {
			$c = $this->charsetConversion->substr('utf-8', $input, $i, 1);
			$encodedString .= $this->encodeCharacter($c);
		}

		return $encodedString;
	}

	/**
	 * Returns backslash encoded numeric format. Does not use backslash
	 * character escapes such as, \" or \' as these may cause parsing problems.
	 * For example, if a javascript attribute, such as onmouseover, contains
	 * a \" that will close the entire attribute and allow an attacker to inject
	 * another script attribute.
	 *
	 * @param string $character utf-8 character that needs to be encoded
	 * @return string encoded character
	 */
	protected function encodeCharacter($character) {
		if ($this->isImmuneCharacter($character)) {
			return $character;
		}

		$ordinalValue = $this->charsetConversion->utf8CharToUnumber($character);

			// Check for alphanumeric characters
		$hex = $this->getHexForNonAlphanumeric($ordinalValue);
		if ($hex === NULL) {
			return $character;
		}

			// Encode up to 256 with \\xHH
		if ($ordinalValue < 256) {
			$pad = substr('00', strlen($hex));
			return '\\x' . $pad . strtoupper($hex);
		}

			// Otherwise encode with \\uHHHH
		$pad = substr('0000', strlen($hex));
		return '\\u' . $pad . strtoupper($hex);
	}

	/**
	 * Checks if the given character is one of the immune characters
	 *
	 * @param string $character utf-8 character to search for, must not be empty
	 * @return boolean TRUE if character is immune, FALSE otherwise
	 */
	protected function isImmuneCharacter($character) {
		return in_array($character, $this->immuneCharacters, TRUE);
	}

	/**
	 * Returns the ordinal value as a hex string of any character that is not a
	 * single-byte alphanumeric. The character should be supplied as a string in
	 * the utf-8 character encoding.
	 * If the character is an alphanumeric character with ordinal value below 255,
	 * then this method will return NULL.
	 *
	 * @param integer $ordinalValue Ordinal value of the character
	 * @return string hexadecimal ordinal value of non-alphanumeric characters or NULL otherwise.
	 */
	protected function getHexForNonAlphanumeric($ordinalValue) {
		if ($ordinalValue <= 255) {
			return $this->hexMatrix[$ordinalValue];
		}
		return dechex($ordinalValue);
	}
}
?>