summaryrefslogtreecommitdiff
path: root/framework/IO/SafeHtml
diff options
context:
space:
mode:
Diffstat (limited to 'framework/IO/SafeHtml')
-rw-r--r--framework/IO/SafeHtml/HTMLSax3.php695
-rw-r--r--framework/IO/SafeHtml/HTMLSax3/Decorators.php363
-rw-r--r--framework/IO/SafeHtml/HTMLSax3/States.php288
-rw-r--r--framework/IO/SafeHtml/license.txt26
-rw-r--r--framework/IO/SafeHtml/readme.txt81
5 files changed, 1453 insertions, 0 deletions
diff --git a/framework/IO/SafeHtml/HTMLSax3.php b/framework/IO/SafeHtml/HTMLSax3.php
new file mode 100644
index 00000000..35e50f55
--- /dev/null
+++ b/framework/IO/SafeHtml/HTMLSax3.php
@@ -0,0 +1,695 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexander Zhukov <alex@veresk.ru> Original port from Python |
+// | Authors: Harry Fuecks <hfuecks@phppatterns.com> Port to PEAR + more |
+// | Authors: Many @ Sitepointforums Advanced PHP Forums |
+// +----------------------------------------------------------------------+
+//
+// $Id: HTMLSax3.php,v 1.2 2005/12/22 11:09:09 weizhuo Exp $
+//
+/**
+* Main parser components
+* @package System.Security.SafeHtml
+* @version $Id: HTMLSax3.php,v 1.2 2005/12/22 11:09:09 weizhuo Exp $
+*/
+/**
+* Required classes
+*/
+
+require_once(dirname(__FILE__).'/HTMLSax3/States.php');
+require_once(dirname(__FILE__).'/HTMLSax3/Decorators.php');
+
+/**
+* Base State Parser
+* @package System.Security.SafeHtml
+* @access protected
+* @abstract
+*/
+class TSax3_StateParser {
+ /**
+ * Instance of user front end class to be passed to callbacks
+ * @var TSax3
+ * @access private
+ */
+ public $htmlsax;
+ /**
+ * User defined object for handling elements
+ * @var object
+ * @access private
+ */
+ public $handler_object_element;
+ /**
+ * User defined open tag handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_opening;
+ /**
+ * User defined close tag handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_closing;
+ /**
+ * User defined object for handling data in elements
+ * @var object
+ * @access private
+ */
+ public $handler_object_data;
+ /**
+ * User defined data handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_data;
+ /**
+ * User defined object for handling processing instructions
+ * @var object
+ * @access private
+ */
+ public $handler_object_pi;
+ /**
+ * User defined processing instruction handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_pi;
+ /**
+ * User defined object for handling JSP/ASP tags
+ * @var object
+ * @access private
+ */
+ public $handler_object_jasp;
+ /**
+ * User defined JSP/ASP handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_jasp;
+ /**
+ * User defined object for handling XML escapes
+ * @var object
+ * @access private
+ */
+ public $handler_object_escape;
+ /**
+ * User defined XML escape handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_escape;
+ /**
+ * User defined handler object or NullHandler
+ * @var object
+ * @access private
+ */
+ public $handler_default;
+ /**
+ * Parser options determining parsing behavior
+ * @var array
+ * @access private
+ */
+ protected $parser_options = array();
+ /**
+ * XML document being parsed
+ * @var string
+ * @access private
+ */
+ protected $rawtext;
+ /**
+ * Position in XML document relative to start (0)
+ * @var int
+ * @access private
+ */
+ protected $position;
+ /**
+ * Length of the XML document in characters
+ * @var int
+ * @access private
+ */
+ protected $length;
+ /**
+ * Array of state objects
+ * @var array
+ * @access private
+ */
+ protected $State = array();
+
+ const TSAX3_STATE_STOP = 0;
+ const TSAX3_STATE_START = 1;
+ const TSAX3_STATE_TAG = 2;
+ const TSAX3_STATE_OPENING_TAG = 3;
+ const TSAX3_STATE_CLOSING_TAG = 4;
+ const TSAX3_STATE_ESCAPE = 6;
+ const TSAX3_STATE_JASP = 7;
+ const TSAX3_STATE_PI = 8;
+
+ /**
+ * Constructs TSax3_StateParser setting up states
+ * @var TSax3 instance of user front end class
+ * @access protected
+ */
+ protected function __construct($htmlsax) {
+ $this->htmlsax = $htmlsax;
+ $this->State[self::TSAX3_STATE_START] = new TSax3_StartingState();
+
+ $this->State[self::TSAX3_STATE_CLOSING_TAG] = new TSax3_ClosingTagState();
+ $this->State[self::TSAX3_STATE_TAG] = new TSax3_TagState();
+ $this->State[self::TSAX3_STATE_OPENING_TAG] = new TSax3_OpeningTagState();
+
+ $this->State[self::TSAX3_STATE_PI] = new TSax3_PiState();
+ $this->State[self::TSAX3_STATE_JASP] = new TSax3_JaspState();
+ $this->State[self::TSAX3_STATE_ESCAPE] = new TSax3_EscapeState();
+ }
+
+ /**
+ * Moves the position back one character
+ * @access protected
+ * @return void
+ */
+ function unscanCharacter() {
+ $this->position -= 1;
+ }
+
+ /**
+ * Moves the position forward one character
+ * @access protected
+ * @return void
+ */
+ function ignoreCharacter() {
+ $this->position += 1;
+ }
+
+ /**
+ * Returns the next character from the XML document or void if at end
+ * @access protected
+ * @return mixed
+ */
+ function scanCharacter() {
+ if ($this->position < $this->length) {
+ return $this->rawtext{$this->position++};
+ }
+ }
+
+ /**
+ * Returns a string from the current position to the next occurance
+ * of the supplied string
+ * @param string string to search until
+ * @access protected
+ * @return string
+ */
+ function scanUntilString($string) {
+ $start = $this->position;
+ $this->position = strpos($this->rawtext, $string, $start);
+ if ($this->position === FALSE) {
+ $this->position = $this->length;
+ }
+ return substr($this->rawtext, $start, $this->position - $start);
+ }
+
+ /**
+ * Returns a string from the current position until the first instance of
+ * one of the characters in the supplied string argument
+ * @param string string to search until
+ * @access protected
+ * @return string
+ * @abstract
+ */
+ function scanUntilCharacters($string) {}
+
+ /**
+ * Moves the position forward past any whitespace characters
+ * @access protected
+ * @return void
+ * @abstract
+ */
+ function ignoreWhitespace() {}
+
+ /**
+ * Begins the parsing operation, setting up any decorators, depending on
+ * parse options invoking _parse() to execute parsing
+ * @param string XML document to parse
+ * @access protected
+ * @return void
+ */
+ function parse($data) {
+ if ($this->parser_options['XML_OPTION_TRIM_DATA_NODES']==1) {
+ $decorator = new TSax3_Trim(
+ $this->handler_object_data,
+ $this->handler_method_data);
+ $this->handler_object_data =& $decorator;
+ $this->handler_method_data = 'trimData';
+ }
+ if ($this->parser_options['XML_OPTION_CASE_FOLDING']==1) {
+ $open_decor = new TSax3_CaseFolding(
+ $this->handler_object_element,
+ $this->handler_method_opening,
+ $this->handler_method_closing);
+ $this->handler_object_element =& $open_decor;
+ $this->handler_method_opening ='foldOpen';
+ $this->handler_method_closing ='foldClose';
+ }
+ if ($this->parser_options['XML_OPTION_LINEFEED_BREAK']==1) {
+ $decorator = new TSax3_Linefeed(
+ $this->handler_object_data,
+ $this->handler_method_data);
+ $this->handler_object_data =& $decorator;
+ $this->handler_method_data = 'breakData';
+ }
+ if ($this->parser_options['XML_OPTION_TAB_BREAK']==1) {
+ $decorator = new TSax3_Tab(
+ $this->handler_object_data,
+ $this->handler_method_data);
+ $this->handler_object_data =& $decorator;
+ $this->handler_method_data = 'breakData';
+ }
+ if ($this->parser_options['XML_OPTION_ENTITIES_UNPARSED']==1) {
+ $decorator = new TSax3_Entities_Unparsed(
+ $this->handler_object_data,
+ $this->handler_method_data);
+ $this->handler_object_data =& $decorator;
+ $this->handler_method_data = 'breakData';
+ }
+ if ($this->parser_options['XML_OPTION_ENTITIES_PARSED']==1) {
+ $decorator = new TSax3_Entities_Parsed(
+ $this->handler_object_data,
+ $this->handler_method_data);
+ $this->handler_object_data =& $decorator;
+ $this->handler_method_data = 'breakData';
+ }
+ // Note switched on by default
+ if ($this->parser_options['XML_OPTION_STRIP_ESCAPES']==1) {
+ $decorator = new TSax3_Escape_Stripper(
+ $this->handler_object_escape,
+ $this->handler_method_escape);
+ $this->handler_object_escape =& $decorator;
+ $this->handler_method_escape = 'strip';
+ }
+ $this->rawtext = $data;
+ $this->length = strlen($data);
+ $this->position = 0;
+ $this->_parse();
+ }
+
+ /**
+ * Performs the parsing itself, delegating calls to a specific parser
+ * state
+ * @param constant state object to parse with
+ * @access protected
+ * @return void
+ */
+ function _parse($state = self::TSAX3_STATE_START) {
+ do {
+ $state = $this->State[$state]->parse($this);
+ } while ($state != self::TSAX3_STATE_STOP &&
+ $this->position < $this->length);
+ }
+}
+
+/**
+* Parser for PHP Versions below 4.3.0. Uses a slower parsing mechanism than
+* the equivalent PHP 4.3.0+ subclass of StateParser
+* @package System.Security.SafeHtml
+* @access protected
+* @see TSax3_StateParser_Gtet430
+*/
+class TSax3_StateParser_Lt430 extends TSax3_StateParser {
+ /**
+ * Constructs TSax3_StateParser_Lt430 defining available
+ * parser options
+ * @var TSax3 instance of user front end class
+ * @access protected
+ */
+ function __construct(& $htmlsax) {
+ parent::__construct($htmlsax);
+ $this->parser_options['XML_OPTION_TRIM_DATA_NODES'] = 0;
+ $this->parser_options['XML_OPTION_CASE_FOLDING'] = 0;
+ $this->parser_options['XML_OPTION_LINEFEED_BREAK'] = 0;
+ $this->parser_options['XML_OPTION_TAB_BREAK'] = 0;
+ $this->parser_options['XML_OPTION_ENTITIES_PARSED'] = 0;
+ $this->parser_options['XML_OPTION_ENTITIES_UNPARSED'] = 0;
+ $this->parser_options['XML_OPTION_STRIP_ESCAPES'] = 0;
+ //var_dump($this->parser_options);
+ }
+
+ /**
+ * Returns a string from the current position until the first instance of
+ * one of the characters in the supplied string argument
+ * @param string string to search until
+ * @access protected
+ * @return string
+ */
+ function scanUntilCharacters($string) {
+ $startpos = $this->position;
+ while ($this->position < $this->length && strpos($string, $this->rawtext{$this->position}) === FALSE) {
+ $this->position++;
+ }
+ return substr($this->rawtext, $startpos, $this->position - $startpos);
+ }
+
+ /**
+ * Moves the position forward past any whitespace characters
+ * @access protected
+ * @return void
+ */
+ function ignoreWhitespace() {
+ while ($this->position < $this->length &&
+ strpos(" \n\r\t", $this->rawtext{$this->position}) !== FALSE) {
+ $this->position++;
+ }
+ }
+
+ /**
+ * Begins the parsing operation, setting up the unparsed XML entities
+ * decorator if necessary then delegating further work to parent
+ * @param string XML document to parse
+ * @access protected
+ * @return void
+ */
+ function parse($data) {
+ parent::parse($data);
+ }
+}
+
+/**
+* Parser for PHP Versions equal to or greater than 4.3.0. Uses a faster
+* parsing mechanism than the equivalent PHP < 4.3.0 subclass of StateParser
+* @package System.Security.SafeHtml
+* @access protected
+* @see TSax3_StateParser_Lt430
+*/
+class TSax3_StateParser_Gtet430 extends TSax3_StateParser {
+ /**
+ * Constructs TSax3_StateParser_Gtet430 defining available
+ * parser options
+ * @var TSax3 instance of user front end class
+ * @access protected
+ */
+ function __construct(& $htmlsax) {
+ parent::__construct($htmlsax);
+ $this->parser_options['XML_OPTION_TRIM_DATA_NODES'] = 0;
+ $this->parser_options['XML_OPTION_CASE_FOLDING'] = 0;
+ $this->parser_options['XML_OPTION_LINEFEED_BREAK'] = 0;
+ $this->parser_options['XML_OPTION_TAB_BREAK'] = 0;
+ $this->parser_options['XML_OPTION_ENTITIES_PARSED'] = 0;
+ $this->parser_options['XML_OPTION_ENTITIES_UNPARSED'] = 0;
+ $this->parser_options['XML_OPTION_STRIP_ESCAPES'] = 0;
+ }
+ /**
+ * Returns a string from the current position until the first instance of
+ * one of the characters in the supplied string argument.
+ * @param string string to search until
+ * @access protected
+ * @return string
+ */
+ function scanUntilCharacters($string) {
+ $startpos = $this->position;
+ $length = strcspn($this->rawtext, $string, $startpos);
+ $this->position += $length;
+ return substr($this->rawtext, $startpos, $length);
+ }
+
+ /**
+ * Moves the position forward past any whitespace characters
+ * @access protected
+ * @return void
+ */
+ function ignoreWhitespace() {
+ $this->position += strspn($this->rawtext, " \n\r\t", $this->position);
+ }
+
+ /**
+ * Begins the parsing operation, setting up the parsed and unparsed
+ * XML entity decorators if necessary then delegating further work
+ * to parent
+ * @param string XML document to parse
+ * @access protected
+ * @return void
+ */
+ function parse($data) {
+ parent::parse($data);
+ }
+}
+
+/**
+* Default NullHandler for methods which were not set by user
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_NullHandler {
+ /**
+ * Generic handler method which does nothing
+ * @access protected
+ * @return void
+ */
+ function DoNothing() {
+ }
+}
+
+/**
+* User interface class. All user calls should only be made to this class
+* @package System.Security.SafeHtml
+* @access public
+*/
+class TSax3 {
+ /**
+ * Instance of concrete subclass of TSax3_StateParser
+ * @var TSax3_StateParser
+ * @access private
+ */
+ private $state_parser;
+
+ /**
+ * Constructs TSax3 selecting concrete StateParser subclass
+ * depending on PHP version being used as well as setting the default
+ * NullHandler for all callbacks<br />
+ * <b>Example:</b>
+ * <pre>
+ * $myHandler = & new MyHandler();
+ * $parser = new TSax3();
+ * $parser->set_object($myHandler);
+ * $parser->set_option('XML_OPTION_CASE_FOLDING');
+ * $parser->set_element_handler('myOpenHandler','myCloseHandler');
+ * $parser->set_data_handler('myDataHandler');
+ * $parser->parser($xml);
+ * </pre>
+ * @access public
+ */
+ function __construct() {
+ if (version_compare(phpversion(), '4.3', 'ge')) {
+ $this->state_parser = new TSax3_StateParser_Gtet430($this);
+ } else {
+ $this->state_parser = new TSax3_StateParser_Lt430($this);
+ }
+ $nullhandler = new TSax3_NullHandler();
+ $this->set_object($nullhandler);
+ $this->set_element_handler('DoNothing', 'DoNothing');
+ $this->set_data_handler('DoNothing');
+ $this->set_pi_handler('DoNothing');
+ $this->set_jasp_handler('DoNothing');
+ $this->set_escape_handler('DoNothing');
+ }
+
+ /**
+ * Sets the user defined handler object. Returns a PEAR Error
+ * if supplied argument is not an object.
+ * @param object handler object containing SAX callback methods
+ * @access public
+ * @return mixed
+ */
+ function set_object(&$object) {
+ if ( is_object($object) ) {
+ $this->state_parser->handler_default =& $object;
+ return true;
+ } else {
+ require_once('PEAR.php');
+ PEAR::raiseError('TSax3::set_object requires '.
+ 'an object instance');
+ }
+ }
+
+ /**
+ * Sets a parser option. By default all options are switched off.
+ * Returns a PEAR Error if option is invalid<br />
+ * <b>Available options:</b>
+ * <ul>
+ * <li>XML_OPTION_TRIM_DATA_NODES: trim whitespace off the beginning
+ * and end of data passed to the data handler</li>
+ * <li>XML_OPTION_LINEFEED_BREAK: linefeeds result in additional data
+ * handler calls</li>
+ * <li>XML_OPTION_TAB_BREAK: tabs result in additional data handler
+ * calls</li>
+ * <li>XML_OPTION_ENTITIES_UNPARSED: XML entities are returned as
+ * seperate data handler calls in unparsed form</li>
+ * <li>XML_OPTION_ENTITIES_PARSED: (PHP 4.3.0+ only) XML entities are
+ * returned as seperate data handler calls and are parsed with
+ * PHP's html_entity_decode() function</li>
+ * <li>XML_OPTION_STRIP_ESCAPES: strips out the -- -- comment markers
+ * or CDATA markup inside an XML escape, if found.</li>
+ * </ul>
+ * To get HTMLSax to behave in the same way as the native PHP SAX parser,
+ * using it's default state, you need to switch on XML_OPTION_LINEFEED_BREAK,
+ * XML_OPTION_ENTITIES_PARSED and XML_OPTION_CASE_FOLDING
+ * @param string name of parser option
+ * @param int (optional) 1 to switch on, 0 for off
+ * @access public
+ * @return boolean
+ */
+ function set_option($name, $value=1) {
+ if ( array_key_exists($name,$this->state_parser->parser_options) ) {
+ $this->state_parser->parser_options[$name] = $value;
+ return true;
+ } else {
+ require_once('PEAR.php');
+ PEAR::raiseError('TSax3::set_option('.$name.') illegal');
+ }
+ }
+
+ /**
+ * Sets the data handler method which deals with the contents of XML
+ * elements.<br />
+ * The handler method must accept two arguments, the first being an
+ * instance of TSax3 and the second being the contents of an
+ * XML element e.g.
+ * <pre>
+ * function myDataHander(& $parser,$data){}
+ * </pre>
+ * @param string name of method
+ * @access public
+ * @return void
+ * @see set_object
+ */
+ function set_data_handler($data_method) {
+ $this->state_parser->handler_object_data =& $this->state_parser->handler_default;
+ $this->state_parser->handler_method_data = $data_method;
+ }
+
+ /**
+ * Sets the open and close tag handlers
+ * <br />The open handler method must accept three arguments; the parser,
+ * the tag name and an array of attributes e.g.
+ * <pre>
+ * function myOpenHander(& $parser,$tagname,$attrs=array()){}
+ * </pre>
+ * The close handler method must accept two arguments; the parser and
+ * the tag name e.g.
+ * <pre>
+ * function myCloseHander(& $parser,$tagname){}
+ * </pre>
+ * @param string name of open method
+ * @param string name of close method
+ * @access public
+ * @return void
+ * @see set_object
+ */
+ function set_element_handler($opening_method, $closing_method) {
+ $this->state_parser->handler_object_element =& $this->state_parser->handler_default;
+ $this->state_parser->handler_method_opening = $opening_method;
+ $this->state_parser->handler_method_closing = $closing_method;
+ }
+
+ /**
+ * Sets the processing instruction handler method e.g. for PHP open
+ * and close tags<br />
+ * The handler method must accept three arguments; the parser, the
+ * PI target and data inside the PI
+ * <pre>
+ * function myPIHander(& $parser,$target, $data){}
+ * </pre>
+ * @param string name of method
+ * @access public
+ * @return void
+ * @see set_object
+ */
+ function set_pi_handler($pi_method) {
+ $this->state_parser->handler_object_pi =& $this->state_parser->handler_default;
+ $this->state_parser->handler_method_pi = $pi_method;
+ }
+
+ /**
+ * Sets the XML escape handler method e.g. for comments and doctype
+ * declarations<br />
+ * The handler method must accept two arguments; the parser and the
+ * contents of the escaped section
+ * <pre>
+ * function myEscapeHander(& $parser, $data){}
+ * </pre>
+ * @param string name of method
+ * @access public
+ * @return void
+ * @see set_object
+ */
+ function set_escape_handler($escape_method) {
+ $this->state_parser->handler_object_escape =& $this->state_parser->handler_default;
+ $this->state_parser->handler_method_escape = $escape_method;
+ }
+
+ /**
+ * Sets the JSP/ASP markup handler<br />
+ * The handler method must accept two arguments; the parser and
+ * body of the JASP tag
+ * <pre>
+ * function myJaspHander(& $parser, $data){}
+ * </pre>
+ * @param string name of method
+ * @access public
+ * @return void
+ * @see set_object
+ */
+ function set_jasp_handler ($jasp_method) {
+ $this->state_parser->handler_object_jasp =& $this->state_parser->handler_default;
+ $this->state_parser->handler_method_jasp = $jasp_method;
+ }
+
+ /**
+ * Returns the current string position of the "cursor" inside the XML
+ * document
+ * <br />Intended for use from within a user defined handler called
+ * via the $parser reference e.g.
+ * <pre>
+ * function myDataHandler(& $parser,$data) {
+ * echo( 'Current position: '.$parser->get_current_position() );
+ * }
+ * </pre>
+ * @access public
+ * @return int
+ * @see get_length
+ */
+ function get_current_position() {
+ return $this->state_parser->position;
+ }
+
+ /**
+ * Returns the string length of the XML document being parsed
+ * @access public
+ * @return int
+ */
+ function get_length() {
+ return $this->state_parser->length;
+ }
+
+ /**
+ * Start parsing some XML
+ * @param string XML document
+ * @access public
+ * @return void
+ */
+ function parse($data) {
+ $this->state_parser->parse($data);
+ }
+}
+?> \ No newline at end of file
diff --git a/framework/IO/SafeHtml/HTMLSax3/Decorators.php b/framework/IO/SafeHtml/HTMLSax3/Decorators.php
new file mode 100644
index 00000000..6256706c
--- /dev/null
+++ b/framework/IO/SafeHtml/HTMLSax3/Decorators.php
@@ -0,0 +1,363 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexander Zhukov <alex@veresk.ru> Original port from Python |
+// | Authors: Harry Fuecks <hfuecks@phppatterns.com> Port to PEAR + more |
+// | Authors: Many @ Sitepointforums Advanced PHP Forums |
+// +----------------------------------------------------------------------+
+//
+// $Id: Decorators.php,v 1.2 2005/12/22 11:09:09 weizhuo Exp $
+//
+/**
+* Decorators for dealing with parser options
+* @package System.Security.SafeHtml
+* @version $Id: Decorators.php,v 1.2 2005/12/22 11:09:09 weizhuo Exp $
+* @see TSax3::set_option
+*/
+/**
+* Trims the contents of element data from whitespace at start and end
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Trim {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_Trim
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Trims the data
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function trimData(&$parser, $data) {
+ $data = trim($data);
+ if ($data != '') {
+ $this->orig_obj->{$this->orig_method}($parser, $data);
+ }
+ }
+}
+/**
+* Coverts tag names to upper case
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_CaseFolding {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original open handler method
+ * @var string
+ * @access private
+ */
+ private $orig_open_method;
+ /**
+ * Original close handler method
+ * @var string
+ * @access private
+ */
+ private $orig_close_method;
+ /**
+ * Constructs TSax3_CaseFolding
+ * @param object handler object being decorated
+ * @param string original open handler method
+ * @param string original close handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_open_method, $orig_close_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_open_method = $orig_open_method;
+ $this->orig_close_method = $orig_close_method;
+ }
+ /**
+ * Folds up open tag callbacks
+ * @param TSax3
+ * @param string tag name
+ * @param array tag attributes
+ * @access protected
+ */
+ function foldOpen(&$parser, $tag, $attrs=array(), $empty = FALSE) {
+ $this->orig_obj->{$this->orig_open_method}($parser, strtoupper($tag), $attrs, $empty);
+ }
+ /**
+ * Folds up close tag callbacks
+ * @param TSax3
+ * @param string tag name
+ * @access protected
+ */
+ function foldClose(&$parser, $tag, $empty = FALSE) {
+ $this->orig_obj->{$this->orig_close_method}($parser, strtoupper($tag), $empty);
+ }
+}
+/**
+* Breaks up data by linefeed characters, resulting in additional
+* calls to the data handler
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Linefeed {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_LineFeed
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Breaks the data up by linefeeds
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function breakData(&$parser, $data) {
+ $data = explode("\n",$data);
+ foreach ( $data as $chunk ) {
+ $this->orig_obj->{$this->orig_method}($parser, $chunk);
+ }
+ }
+}
+/**
+* Breaks up data by tab characters, resulting in additional
+* calls to the data handler
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Tab {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_Tab
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Breaks the data up by linefeeds
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function breakData(&$parser, $data) {
+ $data = explode("\t",$data);
+ foreach ( $data as $chunk ) {
+ $this->orig_obj->{$this->orig_method}($this, $chunk);
+ }
+ }
+}
+/**
+* Breaks up data by XML entities and parses them with html_entity_decode(),
+* resulting in additional calls to the data handler<br />
+* Requires PHP 4.3.0+
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Entities_Parsed {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_Entities_Parsed
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Breaks the data up by XML entities
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function breakData(&$parser, $data) {
+ $data = preg_split('/(&.+?;)/',$data,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+ foreach ( $data as $chunk ) {
+ $chunk = html_entity_decode($chunk,ENT_NOQUOTES);
+ $this->orig_obj->{$this->orig_method}($this, $chunk);
+ }
+ }
+}
+/**
+* Compatibility with older PHP versions
+*/
+if (version_compare(phpversion(), '4.3', '<') && !function_exists('html_entity_decode') ) {
+ function html_entity_decode($str, $style=ENT_NOQUOTES) {
+ return strtr($str,
+ array_flip(get_html_translation_table(HTML_ENTITIES,$style)));
+ }
+}
+/**
+* Breaks up data by XML entities but leaves them unparsed,
+* resulting in additional calls to the data handler<br />
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Entities_Unparsed {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_Entities_Unparsed
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Breaks the data up by XML entities
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function breakData(&$parser, $data) {
+ $data = preg_split('/(&.+?;)/',$data,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+ foreach ( $data as $chunk ) {
+ $this->orig_obj->{$this->orig_method}($this, $chunk);
+ }
+ }
+}
+
+/**
+* Strips the HTML comment markers or CDATA sections from an escape.
+* If XML_OPTIONS_FULL_ESCAPES is on, this decorator is not used.<br />
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Escape_Stripper {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_Entities_Unparsed
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Breaks the data up by XML entities
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function strip(&$parser, $data) {
+ // Check for HTML comments first
+ if ( substr($data,0,2) == '--' ) {
+ $patterns = array(
+ '/^\-\-/', // Opening comment: --
+ '/\-\-$/', // Closing comment: --
+ );
+ $data = preg_replace($patterns,'',$data);
+
+ // Check for XML CDATA sections (note: don't do both!)
+ } else if ( substr($data,0,1) == '[' ) {
+ $patterns = array(
+ '/^\[.*CDATA.*\[/s', // Opening CDATA
+ '/\].*\]$/s', // Closing CDATA
+ );
+ $data = preg_replace($patterns,'',$data);
+ }
+
+ $this->orig_obj->{$this->orig_method}($this, $data);
+ }
+}
+?> \ No newline at end of file
diff --git a/framework/IO/SafeHtml/HTMLSax3/States.php b/framework/IO/SafeHtml/HTMLSax3/States.php
new file mode 100644
index 00000000..2b863a59
--- /dev/null
+++ b/framework/IO/SafeHtml/HTMLSax3/States.php
@@ -0,0 +1,288 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexander Zhukov <alex@veresk.ru> Original port from Python |
+// | Authors: Harry Fuecks <hfuecks@phppatterns.com> Port to PEAR + more |
+// | Authors: Many @ Sitepointforums Advanced PHP Forums |
+// +----------------------------------------------------------------------+
+//
+// $Id: States.php,v 1.2 2005/12/22 11:09:09 weizhuo Exp $
+//
+/**
+* Parsing states.
+* @package System.Security.SafeHtml
+* @version $Id: States.php,v 1.2 2005/12/22 11:09:09 weizhuo Exp $
+*/
+/**
+* Define parser states
+*/
+/*define('TSAX3_STATE_STOP', 0);
+define('TSAX3_STATE_START', 1);
+define('TSAX3_STATE_TAG', 2);
+define('TSAX3_STATE_OPENING_TAG', 3);
+define('TSAX3_STATE_CLOSING_TAG', 4);
+define('TSAX3_STATE_ESCAPE', 6);
+define('TSAX3_STATE_JASP', 7);
+define('TSAX3_STATE_PI', 8);
+*/
+/**
+* StartingState searches for the start of any XML tag
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_StartingState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_TAG
+ * @access protected
+ */
+ function parse(&$context) {
+ $data = $context->scanUntilString('<');
+ if ($data != '') {
+ $context->handler_object_data->
+ {$context->handler_method_data}($context->htmlsax, $data);
+ }
+ $context->IgnoreCharacter();
+ return TSax3_StateParser::TSAX3_STATE_TAG;
+ }
+}
+/**
+* Decides which state to move one from after StartingState
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_TagState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant the next state to move into
+ * @access protected
+ */
+ function parse(&$context) {
+ switch($context->ScanCharacter()) {
+ case '/':
+ return TSax3_StateParser::TSAX3_STATE_CLOSING_TAG;
+ break;
+ case '?':
+ return TSax3_StateParser::TSAX3_STATE_PI;
+ break;
+ case '%':
+ return TSax3_StateParser::TSAX3_STATE_JASP;
+ break;
+ case '!':
+ return TSax3_StateParser::TSAX3_STATE_ESCAPE;
+ break;
+ default:
+ $context->unscanCharacter();
+ return TSax3_StateParser::TSAX3_STATE_OPENING_TAG;
+ }
+ }
+}
+/**
+* Dealing with closing XML tags
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_ClosingTagState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_START
+ * @access protected
+ */
+ function parse(&$context) {
+ $tag = $context->scanUntilCharacters('/>');
+ if ($tag != '') {
+ $char = $context->scanCharacter();
+ if ($char == '/') {
+ $char = $context->scanCharacter();
+ if ($char != '>') {
+ $context->unscanCharacter();
+ }
+ }
+ $context->handler_object_element->
+ {$context->handler_method_closing}($context->htmlsax, $tag, FALSE);
+ }
+ return TSax3_StateParser::TSAX3_STATE_START;
+ }
+}
+/**
+* Dealing with opening XML tags
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_OpeningTagState {
+ /**
+ * Handles attributes
+ * @param string attribute name
+ * @param string attribute value
+ * @return void
+ * @access protected
+ * @see TSax3_AttributeStartState
+ */
+ function parseAttributes(&$context) {
+ $Attributes = array();
+
+ $context->ignoreWhitespace();
+ $attributename = $context->scanUntilCharacters("=/> \n\r\t");
+ while ($attributename != '') {
+ $attributevalue = NULL;
+ $context->ignoreWhitespace();
+ $char = $context->scanCharacter();
+ if ($char == '=') {
+ $context->ignoreWhitespace();
+ $char = $context->ScanCharacter();
+ if ($char == '"') {
+ $attributevalue= $context->scanUntilString('"');
+ $context->IgnoreCharacter();
+ } else if ($char == "'") {
+ $attributevalue = $context->scanUntilString("'");
+ $context->IgnoreCharacter();
+ } else {
+ $context->unscanCharacter();
+ $attributevalue =
+ $context->scanUntilCharacters("> \n\r\t");
+ }
+ } else if ($char !== NULL) {
+ $attributevalue = NULL;
+ $context->unscanCharacter();
+ }
+ $Attributes[$attributename] = $attributevalue;
+
+ $context->ignoreWhitespace();
+ $attributename = $context->scanUntilCharacters("=/> \n\r\t");
+ }
+ return $Attributes;
+ }
+
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_START
+ * @access protected
+ */
+ function parse(&$context) {
+ $tag = $context->scanUntilCharacters("/> \n\r\t");
+ if ($tag != '') {
+ $this->attrs = array();
+ $Attributes = $this->parseAttributes($context);
+ $char = $context->scanCharacter();
+ if ($char == '/') {
+ $char = $context->scanCharacter();
+ if ($char != '>') {
+ $context->unscanCharacter();
+ }
+ $context->handler_object_element->
+ {$context->handler_method_opening}($context->htmlsax, $tag,
+ $Attributes, TRUE);
+ $context->handler_object_element->
+ {$context->handler_method_closing}($context->htmlsax, $tag,
+ TRUE);
+ } else {
+ $context->handler_object_element->
+ {$context->handler_method_opening}($context->htmlsax, $tag,
+ $Attributes, FALSE);
+ }
+ }
+ return TSax3_StateParser::TSAX3_STATE_START;
+ }
+}
+
+/**
+* Deals with XML escapes handling comments and CDATA correctly
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_EscapeState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_START
+ * @access protected
+ */
+ function parse(&$context) {
+ $char = $context->ScanCharacter();
+ if ($char == '-') {
+ $char = $context->ScanCharacter();
+ if ($char == '-') {
+ $context->unscanCharacter();
+ $context->unscanCharacter();
+ $text = $context->scanUntilString('-->');
+ $text .= $context->scanCharacter();
+ $text .= $context->scanCharacter();
+ } else {
+ $context->unscanCharacter();
+ $text = $context->scanUntilString('>');
+ }
+ } else if ( $char == '[') {
+ $context->unscanCharacter();
+ $text = $context->scanUntilString(']>');
+ $text.= $context->scanCharacter();
+ } else {
+ $context->unscanCharacter();
+ $text = $context->scanUntilString('>');
+ }
+
+ $context->IgnoreCharacter();
+ if ($text != '') {
+ $context->handler_object_escape->
+ {$context->handler_method_escape}($context->htmlsax, $text);
+ }
+ return TSax3_StateParser::TSAX3_STATE_START;
+ }
+}
+/**
+* Deals with JASP/ASP markup
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_JaspState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_START
+ * @access protected
+ */
+ function parse(&$context) {
+ $text = $context->scanUntilString('%>');
+ if ($text != '') {
+ $context->handler_object_jasp->
+ {$context->handler_method_jasp}($context->htmlsax, $text);
+ }
+ $context->IgnoreCharacter();
+ $context->IgnoreCharacter();
+ return TSax3_StateParser::TSAX3_STATE_START;
+ }
+}
+/**
+* Deals with XML processing instructions
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_PiState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_START
+ * @access protected
+ */
+ function parse(&$context) {
+ $target = $context->scanUntilCharacters(" \n\r\t");
+ $data = $context->scanUntilString('?>');
+ if ($data != '') {
+ $context->handler_object_pi->
+ {$context->handler_method_pi}($context->htmlsax, $target, $data);
+ }
+ $context->IgnoreCharacter();
+ $context->IgnoreCharacter();
+ return TSax3_StateParser::TSAX3_STATE_START;
+ }
+}
+?> \ No newline at end of file
diff --git a/framework/IO/SafeHtml/license.txt b/framework/IO/SafeHtml/license.txt
new file mode 100644
index 00000000..21496aa2
--- /dev/null
+++ b/framework/IO/SafeHtml/license.txt
@@ -0,0 +1,26 @@
+(c) Roman Ivanov, 2004-2005
+(c) Pixel-Apes ( http://pixel-apes.com/ ), 2004-2005
+(c) JetStyle ( http://jetstyle.ru/ ), 2004-2005
+Maintainer -- Roman Ivanov <thingol@mail.ru>
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/framework/IO/SafeHtml/readme.txt b/framework/IO/SafeHtml/readme.txt
new file mode 100644
index 00000000..d525f92c
--- /dev/null
+++ b/framework/IO/SafeHtml/readme.txt
@@ -0,0 +1,81 @@
+SafeHTML
+--------
+Version 1.3.7.
+http://pixel-apes.com/safehtml/
+--------
+
+This parser strips down all potentially dangerous content within HTML:
+ * opening tag without its closing tag
+ * closing tag without its opening tag
+ * any of these tags: "base", "basefont", "head", "html", "body", "applet", "object",
+ "iframe", "frame", "frameset", "script", "layer", "ilayer", "embed", "bgsound",
+ "link", "meta", "style", "title", "blink", "xml" etc.
+ * any of these attributes: on*, data*, dynsrc
+ * javascript:/vbscript:/about: etc. protocols
+ * expression/behavior etc. in styles
+ * any other active content
+It also tries to convert code to XHTML valid, but htmltidy is far better solution for this task.
+
+If you found any bugs in this parser, please inform me -- ICQ:551593 or mailto:thingol@mail.ru
+
+Please, subscribe to http://pixel-apes.com/safehtml/feed/rss feed in order to receive notices
+when SAFEHTML will be updated.
+
+-- Roman Ivanov.
+-- Pixel-Apes ( http://pixel-apes.com ).
+-- JetStyle ( http://jetstyle.ru/ ).
+
+
+
+--------
+Version history:
+--------
+1.3.7.
+ * Added 'dl' to the list of 'lists' tags.
+ * Added 'callto' to the white list of protocols.
+ * Added white list of "namespaced" attributes.
+1.3.6.
+ * More accurate UTF-7 decoding.
+1.3.5.
+ * Two serious security flaws fixed: UTF-7 XSS and CSS comments handling.
+1.3.2.
+ * Security flaw (improper quotes handling in attributes' values) fixed. Big thanks to Nick Cleaton.
+1.3.1.
+ * Dumb bug fixed (some closing tags were ignored).
+1.3.0.
+ * Two holes (with decimal HTML entities and with \x00 symbol) fixed.
+ * Class rewritten under PEAR coding standarts.
+ * Class now uses unmodified HTMLSax3 from PEAR.
+ * To the list of table tags added: "caption", "col", "colgroup".
+1.2.1.
+ * It was possible to create XSS with hexadecimal HTML entities. Fixed. Big thanks to Christian Stocker.
+1.2.0.
+ * "id" and "name" attributes added to dangerous attributes list, because malefactor can broke legal javascript by spoofing ID or NAME of some element.
+ * New method parse() allows to do all parsing process in two lines of code. Examples also updated.
+ * New array, closeParagraph, contains list of block-level elements. When we open such elemet, we should close paragraph before. . It allows SafeHTML to produce more XHTML compliant code.
+ * Added "webcal" to white list of protocols for those who uses calendar programs (Mozilla/iCal/etc).
+ * Now SafeHTML strips down table elements when we are not inside table.
+ * Now SafeHTML correctly closes unclosed "li" tags: before opening "li" of the same nesting level.
+1.1.0.
+ * New "dangerous" protocols: hcp, ms-help, help, disk, vnd.ms.radio, opera, res, resource, chrome, mocha, livescript.
+ * <XML> tag was moved from "tags for deletion" to "tags for deletion with content".
+ * New "dangerous" CSS instruction "include-source" (NN4 specific).
+ * New array, Attributes, contains list of attributes for removal. If you need to remove "id" or "name" attribute,
+ just add it to this array.
+ * Now it is possible to choose between white-list and black-list filtering of protocols. Defaults are "white-list".
+ This list is: "http", "https", "ftp", "telnet", "news", "nntp", "gopher", "mailto", "file".
+ * For speed purposes, we now filter protocols only from these attributes: src, href, action, lowsrc, dynsrc,
+ background, codebase.
+ * Opera6 XSS bug ([\xC0][\xBC]script>alert(1)[\xC0][\xBC]/script> [UTF-8] workarounded.
+1.0.4.
+ New "dangerous" tag: plaintext.
+1.0.3.
+ Added array of elements that can have no closing tag.
+1.0.2.
+ Bug fix: <img src="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;alert(1);"> attack.
+ Thanks to shmel.
+1.0.1.
+ Bug fix: safehtml hangs on <style></style></style> code.
+ Thanks to lj user=electrocat.
+1.0.0.
+ First public release