diff options
| -rw-r--r-- | .gitattributes | 6 | ||||
| -rw-r--r-- | framework/I18N/TGlobalization.php | 45 | ||||
| -rw-r--r-- | framework/IO/SafeHtml/HTMLSax3.php | 695 | ||||
| -rw-r--r-- | framework/IO/SafeHtml/HTMLSax3/Decorators.php | 363 | ||||
| -rw-r--r-- | framework/IO/SafeHtml/HTMLSax3/States.php | 288 | ||||
| -rw-r--r-- | framework/IO/SafeHtml/license.txt | 26 | ||||
| -rw-r--r-- | framework/IO/SafeHtml/readme.txt | 81 | ||||
| -rw-r--r-- | framework/IO/TSafeHtml.php | 670 | ||||
| -rw-r--r-- | framework/Log/ILog.php | 2 | ||||
| -rw-r--r-- | framework/Log/TLogManager.php | 4 | ||||
| -rw-r--r-- | framework/Web/THttpResponse.php | 68 | ||||
| -rw-r--r-- | framework/Web/UI/TTemplateManager.php | 4 | ||||
| -rw-r--r-- | tests/FunctionalTests/README.txt | 6 | ||||
| -rw-r--r-- | tests/FunctionalTests/index.php | 5 | ||||
| -rw-r--r-- | tests/FunctionalTests/protected/application.xml | 18 | ||||
| -rw-r--r-- | tests/FunctionalTests/protected/pages/I18N/BasicI18N.page | 3 | ||||
| -rw-r--r-- | tests/FunctionalTests/protected/pages/I18N/BasicI18N.php | 13 | ||||
| -rw-r--r-- | tests/FunctionalTests/protected/runtime/config.cache | bin | 1543 -> 2357 bytes | |||
| -rw-r--r-- | tests/FunctionalTests/selenium/php/selenium.php | 5 | 
19 files changed, 2243 insertions, 59 deletions
diff --git a/.gitattributes b/.gitattributes index 1bff7aa5..b93059f7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -409,6 +409,12 @@ framework/I18N/core/data/zh_TW.dat -text  framework/I18N/core/util.php -text  framework/I18N/schema/mysql.sql -text  framework/I18N/schema/sqlite.sql -text +framework/IO/SafeHtml/HTMLSax3.php -text +framework/IO/SafeHtml/HTMLSax3/Decorators.php -text +framework/IO/SafeHtml/HTMLSax3/States.php -text +framework/IO/SafeHtml/license.txt -text +framework/IO/SafeHtml/readme.txt -text +framework/IO/TSafeHtml.php -text  framework/IO/TTextWriter.php -text  framework/Log/EventLog/context.php -text  framework/Log/EventLog/exceptions/file_exception.php -text diff --git a/framework/I18N/TGlobalization.php b/framework/I18N/TGlobalization.php index 4e6d030c..0900943e 100644 --- a/framework/I18N/TGlobalization.php +++ b/framework/I18N/TGlobalization.php @@ -12,7 +12,7 @@  /**
 - * TGlobalization contains settings for Culture, Charset, ContentType
 + * TGlobalization contains settings for Culture, Charset
   * and TranslationConfiguration.
   *
   * TGlobalization can be subclassed to change how the Culture, Charset
 @@ -39,12 +39,6 @@ class TGlobalization extends TModule  	private $_defaultCulture = 'en';
  	/**
 -	 * Default content type is 'text/html'
 -	 * @var ${type}
 -	 */
 -	private $_defaultContentType = 'text/html';
 -
 -	/**
  	 * Translation source parameters.
  	 * @var TMap
  	 */
 @@ -63,12 +57,6 @@ class TGlobalization extends TModule  	protected $_culture='en';
  	/**
 -	 * The content type for the http header
 -	 * @var string
 -	 */
 -	protected $_contentType='text/html';
 -
 -	/**
  	 * Initialize the Culture and Charset for this application.
  	 * You should override this method if you want a different way of
  	 * setting the Culture and/or Charset for your application.
 @@ -77,7 +65,6 @@ class TGlobalization extends TModule  	 */
  	public function init($xml)
  	{		
 -		$this->_defaultContentType = $this->getContentType();
  		$this->_defaultCharset = $this->getCharset();
  		$this->_defaultCulture = $this->getCulture();
 @@ -86,36 +73,38 @@ class TGlobalization extends TModule  		$this->getApplication()->setGlobalization($this);
  	}
 +	/**
 +	 * @return string current application culture
 +	 */
  	public function getCulture()
  	{
  		return $this->_culture;
  	}
 +	/**
 +	 * @param string culture, e.g. <tt>en_US</tt> for American English 
 +	 */
  	public function setCulture($culture)
  	{
  		$this->_culture = str_replace('-','_',$culture);
  	}
 +	/**
 +	 * @return string localization charset
 +	 */
  	public function getCharset()
  	{
  		return $this->_charset;
  	}
 +	/**
 +	 * @param string localization charset, e.g. <tt>UTF-8</tt>
 +	 */
  	public function setCharset($charset)
  	{
  		$this->_charset = $charset;
  	}
 -	public function setContentType($type)
 -	{
 -		$this->_contentType = $type;
 -	}
 -
 -	public function getContentType()
 -	{
 -		return $this->_contentType;
 -	}
 -
  	/**
  	 * @return TMap translation source configuration.
  	 */
 @@ -167,14 +156,6 @@ class TGlobalization extends TModule  	}
  	/**
 -	 * @return string default content-type set in application.xml
 -	 */
 -	public function getDefaultContentType()
 -	{
 -		return $this->_defaultContentType;
 -	}
 -
 -	/**
  	 * Gets all the variants of a specific culture. If the parameter
  	 * $culture is null, the current culture is used.
  	 * @param string $culture the Culture string
 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="javascript: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
 diff --git a/framework/IO/TSafeHtml.php b/framework/IO/TSafeHtml.php new file mode 100644 index 00000000..311ae2a3 --- /dev/null +++ b/framework/IO/TSafeHtml.php @@ -0,0 +1,670 @@ +<?php
 +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 +
 +/**
 + * SafeHTML Parser
 + *
 + * PHP versions 4 and 5
 + *
 + * @category   HTML
 + * @package    System.Security
 + * @author     Roman Ivanov <thingol@mail.ru>
 + * @copyright  2004-2005 Roman Ivanov
 + * @license    http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
 + * @version    1.3.7
 + * @link       http://pixel-apes.com/safehtml/
 + */
 +
 +
 +/**
 + * This package requires HTMLSax3 package
 + */
 +Prado::using('System.IO..SafeHtml.HTMLSax3');
 +
 + 
 +/**
 + *
 + * SafeHTML Parser
 + *
 + * This parser strips down all potentially dangerous content within HTML:
 + * <ul>
 + * <li>opening tag without its closing tag</li>
 + * <li>closing tag without its opening tag</li>
 + * <li>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.</li>
 + * <li>any of these attributes: on*, data*, dynsrc</li>
 + * <li>javascript:/vbscript:/about: etc. protocols</li>
 + * <li>expression/behavior etc. in styles</li>
 + * <li>any other active content</li>
 + * </ul>
 + * It also tries to convert code to XHTML valid, but htmltidy is far better 
 + * solution for this task.
 + *
 + * <b>Example:</b>
 + * <pre>
 + * $parser =& new SafeHTML();
 + * $result = $parser->parse($doc);
 + * </pre>
 + *
 + * @category   HTML
 + * @package    System.Security
 + * @author     Roman Ivanov <thingol@mail.ru>
 + * @copyright  1997-2005 Roman Ivanov
 + * @license    http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
 + * @version    Release: @package_version@
 + * @link       http://pear.php.net/package/SafeHTML
 + */
 +class TSafeHtml
 +{
 +    /**
 +     * Storage for resulting HTML output
 +     *
 +     * @var string
 +     * @access private
 +     */
 +    private $_xhtml = '';
 +    
 +    /**
 +     * Array of counters for each tag
 +     *
 +     * @var array
 +     * @access private
 +     */
 +    private $_counter = array();
 +    
 +    /**
 +     * Stack of unclosed tags
 +     *
 +     * @var array
 +     * @access private
 +     */
 +    private $_stack = array();
 +    
 +    /**
 +     * Array of counters for tags that must be deleted with all content
 +     *
 +     * @var array
 +     * @access private
 +     */
 +    private $_dcCounter = array();
 +    
 +    /**
 +     * Stack of unclosed tags that must be deleted with all content
 +     *
 +     * @var array
 +     * @access private
 +     */
 +    private $_dcStack = array();
 +    
 +    /**
 +     * Stores level of list (ol/ul) nesting
 +     *
 +     * @var int
 +     * @access private
 +     */
 +    private $_listScope = 0; 
 +    
 +    /**
 +     * Stack of unclosed list tags 
 +     *
 +     * @var array
 +     * @access private
 +     */
 +    private $_liStack = array();
 +
 +    /**
 +     * Array of prepared regular expressions for protocols (schemas) matching
 +     *
 +     * @var array
 +     * @access private
 +     */
 +    private $_protoRegexps = array();
 +    
 +    /**
 +     * Array of prepared regular expressions for CSS matching
 +     *
 +     * @var array
 +     * @access private
 +     */
 +    private $_cssRegexps = array();
 +
 +    /**
 +     * List of single tags ("<tag />")
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $singleTags = array('area', 'br', 'img', 'input', 'hr', 'wbr', );
 +
 +    /**
 +     * List of dangerous tags (such tags will be deleted)
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $deleteTags = array(
 +        'applet', 'base',   'basefont', 'bgsound', 'blink',  'body', 
 +        'embed',  'frame',  'frameset', 'head',    'html',   'ilayer', 
 +        'iframe', 'layer',  'link',     'meta',    'object', 'style', 
 +        'title',  'script', 
 +        );
 +
 +    /**
 +     * List of dangerous tags (such tags will be deleted, and all content 
 +     * inside this tags will be also removed)
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $deleteTagsContent = array('script', 'style', 'title', 'xml', );
 +
 +    /**
 +     * Type of protocols filtering ('white' or 'black')
 +     *
 +     * @var string
 +     * @access public
 +     */
 +    public $protocolFiltering = 'white';
 +
 +    /**
 +     * List of "dangerous" protocols (used for blacklist-filtering)
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $blackProtocols = array(
 +        'about',   'chrome',     'data',       'disk',     'hcp',     
 +        'help',    'javascript', 'livescript', 'lynxcgi',  'lynxexec', 
 +        'ms-help', 'ms-its',     'mhtml',      'mocha',    'opera',   
 +        'res',     'resource',   'shell',      'vbscript', 'view-source', 
 +        'vnd.ms.radio',          'wysiwyg', 
 +        );
 +
 +    /**
 +     * List of "safe" protocols (used for whitelist-filtering)
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $whiteProtocols = array(
 +        'ed2k',   'file', 'ftp',  'gopher', 'http',  'https', 
 +        'irc',    'mailto', 'news', 'nntp', 'telnet', 'webcal', 
 +        'xmpp',   'callto',
 +        );
 +
 +    /**
 +     * List of attributes that can contain protocols
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $protocolAttributes = array(
 +        'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc', 'src', 
 +        );
 +
 +    /**
 +     * List of dangerous CSS keywords
 +     *
 +     * Whole style="" attribute will be removed, if parser will find one of 
 +     * these keywords
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $cssKeywords = array(
 +        'absolute', 'behavior',       'behaviour',   'content', 'expression', 
 +        'fixed',    'include-source', 'moz-binding',
 +        );
 +
 +    /**
 +     * List of tags that can have no "closing tag"
 +     *
 +     * @var array
 +     * @access public
 +     * @deprecated XHTML does not allow such tags
 +     */
 +    public $noClose = array();
 +
 +    /**
 +     * List of block-level tags that terminates paragraph
 +     *
 +     * Paragraph will be closed when this tags opened
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $closeParagraph = array(
 +        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
 +        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
 +        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
 +        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
 +        'table',   'ul',         'xmp', 
 +        );
 +
 +    /**
 +     * List of table tags, all table tags outside a table will be removed
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $tableTags = array(
 +        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
 +        'thead',   'tr', 
 +        );
 +
 +    /**
 +     * List of list tags
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $listTags = array('dir', 'menu', 'ol', 'ul', 'dl', );
 +
 +    /**
 +     * List of dangerous attributes
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $attributes = array('dynsrc', 'id', 'name', );
 +
 +    /**
 +     * List of allowed "namespaced" attributes
 +     *
 +     * @var array
 +     * @access public
 +     */
 +    public $attributesNS = array('xml:lang', );
 +
 +    /**
 +     * Constructs class
 +     *
 +     * @access public
 +     */
 +    public function __construct() 
 +    {
 +        //making regular expressions based on Proto & CSS arrays
 +        foreach ($this->blackProtocols as $proto) {
 +            $preg = "/[\s\x01-\x1F]*";
 +            for ($i=0; $i<strlen($proto); $i++) {
 +                $preg .= $proto{$i} . "[\s\x01-\x1F]*";
 +            }
 +            $preg .= ":/i";
 +            $this->_protoRegexps[] = $preg;
 +        }
 +
 +        foreach ($this->cssKeywords as $css) {
 +            $this->_cssRegexps[] = '/' . $css . '/i';
 +        }
 +        return true;
 +    }
 +
 +    /**
 +     * Handles the writing of attributes - called from $this->_openHandler()
 +     *
 +     * @param array $attrs array of attributes $name => $value
 +     * @return boolean
 +     * @access private
 +     */
 +    private function _writeAttrs ($attrs) 
 +    {
 +        if (is_array($attrs)) {
 +            foreach ($attrs as $name => $value) {
 +
 +                $name = strtolower($name);
 +
 +                if (strpos($name, 'on') === 0) {
 +                    continue;
 +                }
 +                if (strpos($name, 'data') === 0) {
 +                    continue;
 +                }
 +                if (in_array($name, $this->attributes)) {
 +                    continue;
 +                }
 +                if (!preg_match("/^[a-z0-9]+$/i", $name)) {
 +                    if (!in_array($name, $this->attributesNS))
 +                    {
 +                        continue;
 +                    }
 +                }
 +
 +                if (($value === TRUE) || (is_null($value))) {
 +                    $value = $name;
 +                }
 +
 +                if ($name == 'style') {
 +                   
 +                   // removes insignificant backslahes
 +                   $value = str_replace("\\", '', $value);
 +
 +                   // removes CSS comments
 +                   while (1)
 +                   {
 +                     $_value = preg_replace("!/\*.*?\*/!s", '', $value);
 +                     if ($_value == $value) break;
 +                     $value = $_value;
 +                   }
 +                   
 +                   // replace all & to &
 +                   $value = str_replace('&', '&', $value);
 +                   $value = str_replace('&', '&', $value);
 +
 +                   foreach ($this->_cssRegexps as $css) {
 +                       if (preg_match($css, $value)) { 
 +                           continue 2;
 +                       }
 +                   }
 +                   foreach ($this->_protoRegexps as $proto) {
 +                       if (preg_match($proto, $value)) {
 +                           continue 2;
 +                       }
 +                   }
 +                }
 +
 +                $tempval = preg_replace('/&#(\d+);?/me', "chr('\\1')", $value); //"'
 +                $tempval = preg_replace('/&#x([0-9a-f]+);?/mei', "chr(hexdec('\\1'))", $tempval);
 +
 +                if ((in_array($name, $this->protocolAttributes)) && 
 +                    (strpos($tempval, ':') !== false)) 
 +                {
 +                    if ($this->protocolFiltering == 'black') {
 +                        foreach ($this->_protoRegexps as $proto) {
 +                            if (preg_match($proto, $tempval)) continue 2;
 +                        }
 +                    } else {
 +                        $_tempval = explode(':', $tempval);
 +                        $proto = $_tempval[0];
 +                        if (!in_array($proto, $this->whiteProtocols)) {
 +                            continue;
 +                        }
 +                    }
 +                }
 +
 +                $value = str_replace("\"", """, $value);
 +                $this->_xhtml .= ' ' . $name . '="' . $value . '"';
 +            }
 +        }
 +        return true;
 +    }
 +
 +    /**
 +     * Opening tag handler - called from HTMLSax
 +     *
 +     * @param object $parser HTML Parser
 +     * @param string $name   tag name
 +     * @param array  $attrs  tag attributes
 +     * @return boolean
 +     * @access private
 +     */
 +    public function _openHandler(&$parser, $name, $attrs) 
 +    {
 +        $name = strtolower($name);
 +
 +        if (in_array($name, $this->deleteTagsContent)) {
 +            array_push($this->_dcStack, $name);
 +            $this->_dcCounter[$name] = isset($this->_dcCounter[$name]) ? $this->_dcCounter[$name]+1 : 1;
 +        }
 +        if (count($this->_dcStack) != 0) {
 +            return true;
 +        }
 +
 +        if (in_array($name, $this->deleteTags)) {
 +            return true;
 +        }
 +        
 +        if (!preg_match("/^[a-z0-9]+$/i", $name)) {
 +            if (preg_match("!(?:\@|://)!i", $name)) {
 +                $this->_xhtml .= '<' . $name . '>';
 +            }
 +            return true;
 +        }
 +
 +        if (in_array($name, $this->singleTags)) {
 +            $this->_xhtml .= '<' . $name;
 +            $this->_writeAttrs($attrs);
 +            $this->_xhtml .= ' />';
 +            return true;
 +        }
 +
 +        // TABLES: cannot open table elements when we are not inside table
 +        if ((isset($this->_counter['table'])) && ($this->_counter['table'] <= 0) 
 +            && (in_array($name, $this->tableTags))) 
 +        {
 +            return true;
 +        }
 +
 +        // PARAGRAPHS: close paragraph when closeParagraph tags opening
 +        if ((in_array($name, $this->closeParagraph)) && (in_array('p', $this->_stack))) {
 +            $this->_closeHandler($parser, 'p');
 +        }
 +
 +        // LISTS: we should close <li> if <li> of the same level opening
 +        if ($name == 'li' && count($this->_liStack) && 
 +            $this->_listScope == $this->_liStack[count($this->_liStack)-1]) 
 +        {
 +            $this->_closeHandler($parser, 'li');
 +        }
 +
 +        // LISTS: we want to know on what nesting level of lists we are
 +        if (in_array($name, $this->listTags)) {
 +            $this->_listScope++;
 +        }
 +        if ($name == 'li') {
 +            array_push($this->_liStack, $this->_listScope);
 +        }
 +            
 +        $this->_xhtml .= '<' . $name;
 +        $this->_writeAttrs($attrs);
 +        $this->_xhtml .= '>';
 +        array_push($this->_stack,$name);
 +        $this->_counter[$name] = isset($this->_counter[$name]) ? $this->_counter[$name]+1 : 1;
 +        return true;
 +    }
 +
 +    /**
 +     * Closing tag handler - called from HTMLSax
 +     *
 +     * @param object $parsers HTML parser
 +     * @param string $name    tag name
 +     * @return boolean
 +     * @access private
 +     */
 +    public function _closeHandler(&$parser, $name) 
 +    {
 +
 +        $name = strtolower($name);
 +
 +        if (isset($this->_dcCounter[$name]) && ($this->_dcCounter[$name] > 0) && 
 +            (in_array($name, $this->deleteTagsContent))) 
 +        {
 +           while ($name != ($tag = array_pop($this->_dcStack))) {
 +            $this->_dcCounter[$tag]--;
 +           }
 +
 +           $this->_dcCounter[$name]--;
 +        }
 +
 +        if (count($this->_dcStack) != 0) {
 +            return true;
 +        }
 +
 +        if ((isset($this->_counter[$name])) && ($this->_counter[$name] > 0)) {
 +           while ($name != ($tag = array_pop($this->_stack))) {
 +               $this->_closeTag($tag);
 +           }
 +
 +           $this->_closeTag($name);
 +        }
 +        return true;
 +    }
 +
 +    /**
 +     * Closes tag 
 +     *
 +     * @param string $tag tag name
 +     * @return boolean
 +     * @access private
 +     */
 +    public function _closeTag($tag) 
 +    {
 +        if (!in_array($tag, $this->noClose)) {
 +            $this->_xhtml .= '</' . $tag . '>';
 +        }
 +
 +        $this->_counter[$tag]--;
 +
 +        if (in_array($tag, $this->listTags)) {
 +            $this->_listScope--;
 +        }
 +
 +        if ($tag == 'li') {
 +            array_pop($this->_liStack);
 +        }
 +        return true;
 +    }
 +
 +    /**
 +     * Character data handler - called from HTMLSax
 +     *
 +     * @param object $parser HTML parser
 +     * @param string $data   textual data
 +     * @return boolean
 +     * @access private
 +     */
 +    public function _dataHandler(&$parser, $data) 
 +    {
 +        if (count($this->_dcStack) == 0) {
 +            $this->_xhtml .= $data;
 +        }
 +        return true;
 +    }
 +
 +    /**
 +     * Escape handler - called from HTMLSax
 +     *
 +     * @param object $parser HTML parser
 +     * @param string $data   comments or other type of data
 +     * @return boolean
 +     * @access private
 +     */
 +    public function _escapeHandler(&$parser, $data) 
 +    {
 +        return true;
 +    }
 +
 +    /**
 +     * Returns the XHTML document
 +     *
 +     * @return string Processed (X)HTML document
 +     * @access public
 +     */
 +    public function getXHTML () 
 +    {
 +        while ($tag = array_pop($this->_stack)) {
 +            $this->_closeTag($tag);
 +        }
 +        
 +        return $this->_xhtml;
 +    }
 +
 +    /**
 +     * Clears current document data
 +     *
 +     * @return boolean
 +     * @access public
 +     */
 +    public function clear() 
 +    {
 +        $this->_xhtml = '';
 +        return true;
 +    }
 +
 +    /**
 +     * Main parsing fuction
 +     *
 +     * @param string $doc HTML document for processing
 +     * @return string Processed (X)HTML document
 +     * @access public
 +     */
 +    public function parse($doc) 
 +    {
 +
 +       // Save all '<' symbols
 +       $doc = preg_replace("/<(?=[^a-zA-Z\/\!\?\%])/", '<', (string)$doc);
 +
 +       // Web documents shouldn't contains \x00 symbol
 +       $doc = str_replace("\x00", '', $doc);
 +
 +       // Opera6 bug workaround
 +       $doc = str_replace("\xC0\xBC", '<', $doc);
 +       
 +       // UTF-7 encoding ASCII decode
 +       $doc = $this->repackUTF7($doc);
 +
 +       // Instantiate the parser
 +       $parser= new TSax3();
 +
 +       // Set up the parser
 +       $parser->set_object($this);
 +
 +       $parser->set_element_handler('_openHandler','_closeHandler');
 +       $parser->set_data_handler('_dataHandler');
 +       $parser->set_escape_handler('_escapeHandler');
 +
 +       $parser->parse($doc);
 +
 +       return $this->getXHTML();
 +
 +    }
 +
 +
 +    /**
 +     * UTF-7 decoding fuction
 +     *
 +     * @param string $str HTML document for recode ASCII part of UTF-7 back to ASCII
 +     * @return string Decoded document
 +     * @access private
 +     */
 +    private function repackUTF7($str)
 +    {
 +       return preg_replace_callback('!\+([0-9a-zA-Z/]+)\-!', array($this, 'repackUTF7Callback'), $str);
 +    }
 +
 +    /**
 +     * Additional UTF-7 decoding fuction
 +     *
 +     * @param string $str String for recode ASCII part of UTF-7 back to ASCII
 +     * @return string Recoded string
 +     * @access private
 +     */
 +    private function repackUTF7Callback($str)
 +    {
 +       $str = base64_decode($str[1]);
 +       $str = preg_replace_callback('/^((?:\x00.)*)((?:[^\x00].)+)/', array($this, 'repackUTF7Back'), $str);
 +       return preg_replace('/\x00(.)/', '$1', $str);
 +    }
 +
 +    /**
 +     * Additional UTF-7 encoding fuction
 +     *
 +     * @param string $str String for recode ASCII part of UTF-7 back to ASCII
 +     * @return string Recoded string
 +     * @access private
 +     */
 +    private function repackUTF7Back($str)
 +    {
 +       return $str[1].'+'.rtrim(base64_encode($str[2]), '=').'-';
 +    }
 +}
 +
 +/*
 + * Local variables:
 + * tab-width: 4
 + * c-basic-offset: 4
 + * c-hanging-comment-ender-p: nil
 + * End:
 + */
 +
 +?>
\ No newline at end of file diff --git a/framework/Log/ILog.php b/framework/Log/ILog.php index faeb4ff3..90239e97 100644 --- a/framework/Log/ILog.php +++ b/framework/Log/ILog.php @@ -12,6 +12,8 @@ interface ILog  require_once(PRADO_DIR.'/Log/EventLog/log.php');
  require_once(PRADO_DIR.'/Log/EventLog/exceptions/writer_exception.php');
 +require_once(PRADO_DIR.'/Log/EventLog/exceptions/file_exception.php');
 +
  /**
   * ${classname}
   *
 diff --git a/framework/Log/TLogManager.php b/framework/Log/TLogManager.php index 8ab170ed..dd926bc7 100644 --- a/framework/Log/TLogManager.php +++ b/framework/Log/TLogManager.php @@ -110,6 +110,10 @@ class TEzcLoggerUnixFileWriterFactory  		$path = $xml->getAttribute('directory');
  		$dir = Prado::getPathOfNamespace($path);
 +		if(!is_dir($dir))
 +			throw new TException("missing_logging_directory $dir");
 +		if(!is_writable($dir))
 +			throw new TException("unable_to_writer_to $dir");
  		$file = $xml->getAttribute('filename');
  		if(empty($file)) $file = 'prado.log';
  		return new ezcLogWriterUnixFile($dir, $file);
 diff --git a/framework/Web/THttpResponse.php b/framework/Web/THttpResponse.php index ecaf59d0..cf44d19d 100644 --- a/framework/Web/THttpResponse.php +++ b/framework/Web/THttpResponse.php @@ -32,6 +32,9 @@   * where {@link getCacheExpire CacheExpire}, {@link getCacheControl CacheControl}
   * and {@link getBufferOutput BufferOutput} are configurable properties of THttpResponse.
   *
 + * When sending headers the Charset set in {@link TGlobalization::getCharset()} 
 + * is use when Charset is null or empty in THttpResponse.
 + * 
   * @author Qiang Xue <qiang.xue@gmail.com>
   * @version $Revision: $  $Date: $
   * @package System.Web
 @@ -59,6 +62,15 @@ class THttpResponse extends TModule implements ITextWriter  	 * @var string HTML writer type
  	 */
  	private $_htmlWriterType='System.Web.UI.THtmlWriter';
 +	/**
 +	 * @var string content type
 +	 */
 +	private $_contentType='text/html';
 +
 +	/**
 +	 * @var string character set, e.g. UTF-8
 +	 */
 +	private $_charset;
  	/**
  	 * Destructor.
 @@ -119,6 +131,38 @@ class THttpResponse extends TModule implements ITextWriter  	}
  	/**
 +	 * @string content type, default is text/html
 +	 */
 +	public function setContentType($type)
 +	{
 +		$this->_contentType = $type;
 +	}
 +
 +	/**
 +	 * @return string current content type
 +	 */
 +	public function getContentType()
 +	{
 +		return $this->_contentType;
 +	}
 +	
 +	/**
 +	 * @return string output charset.
 +	 */
 +	public function getCharset()
 +	{
 +		return $this->_charset;
 +	}
 +
 +	/**
 +	 * @param string output charset.
 +	 */
 +	public function setCharset($charset)
 +	{
 +		$this->_charset = $charset;
 +	}
 +
 +	/**
  	 * @return boolean whether to enable output buffer
  	 */
  	public function getBufferOutput()
 @@ -226,13 +270,28 @@ class THttpResponse extends TModule implements ITextWriter  	}
  	/**
 -	 * Outputs the buffered content.
 +	 * Outputs the buffered content, sends content-type and charset header.
  	 */
  	public function flush()
  	{
 +		$header = $this->getContentTypeHeader();
 +		$this->appendHeader($header);
  		if($this->_bufferOutput)
  			ob_flush();
 -		Prado::coreLog("Flushing output");
 +		Prado::coreLog("Flushing output $header");
 +	}
 +
 +	/**
 +	 * @return string content type and charset header
 +	 */
 +	protected function getContentTypeHeader()
 +	{
 +		$app = $this->getApplication()->getGlobalization();		
 +		$charset = $this->getCharset();
 +		if(empty($charset))
 +			$charset = !is_null($app) ? $app->getCharset() : 'UTF-8';
 +		$type = $this->getContentType();
 +		return "Content-Type: $type; charset=$charset";
  	}
  	/**
 @@ -254,6 +313,11 @@ class THttpResponse extends TModule implements ITextWriter  		header($value);
  	}
 +	public function sendContentTypeHeader($type=null)
 +	{
 +
 +	}
 +
  	/**
  	 * Writes a log message into error log.
  	 * This method is simple wrapper of PHP function error_log.
 diff --git a/framework/Web/UI/TTemplateManager.php b/framework/Web/UI/TTemplateManager.php index d5c5f0ba..df708e50 100644 --- a/framework/Web/UI/TTemplateManager.php +++ b/framework/Web/UI/TTemplateManager.php @@ -60,7 +60,7 @@ class TTemplateManager extends TModule  	public function getTemplateByClassName($className)
  	{
  		$class=new ReflectionClass($className);
 -		$tplFile=dirname($class->getFileName()).'/'.$className.self::TEMPLATE_FILE_EXT;
 +		$tplFile=dirname($class->getFileName()).'/'.$className.self::TEMPLATE_FILE_EXT;		
  		return $this->getTemplateByFileName($tplFile);
  	}
 @@ -72,6 +72,7 @@ class TTemplateManager extends TModule  	{
  		if(!is_null($fileName=$this->getLocalizedTemplate($fileName)))
  		{
 +			Prado::coreLog("Loading template $fileName");
  			if(($cache=$this->getApplication()->getCache())===null)
  				return new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName);
  			else
 @@ -106,6 +107,7 @@ class TTemplateManager extends TModule  			if(($file=realpath($file))!==false && is_file($file))
  				return $file;
  		}
 +
  	}
  }
 diff --git a/tests/FunctionalTests/README.txt b/tests/FunctionalTests/README.txt index 189f69ad..06519092 100644 --- a/tests/FunctionalTests/README.txt +++ b/tests/FunctionalTests/README.txt @@ -48,11 +48,11 @@ class testMyButtonExample extends SeleniumTestCase  {
  	function setup()
  	{
 -		//initializes $this->Page to be MyButtonExample instance
 -		$this->initPage(__FILE__);
 +		//get the test page url
 +		$page = Prado::getApplication()->getTestPage(__FILE__);
  		//open MyButtonExample page
 -		$this->open($this->Page->Request->TestUrl);				
 +		$this->open($page);				
  	}
  	function testButtonClick()
 diff --git a/tests/FunctionalTests/index.php b/tests/FunctionalTests/index.php index 442c4bbf..da60b2f0 100644 --- a/tests/FunctionalTests/index.php +++ b/tests/FunctionalTests/index.php @@ -1,7 +1,7 @@  <?php  require('config.php'); - +header("Content-Type: text/html; charset=UTF-8");  class BrowserTestConfig extends PradoTestConfig  {  	//functional test groups @@ -17,7 +17,8 @@ class BrowserTestConfig extends PradoTestConfig  		$groups[] = realpath($base);  		$dirs = new DirectoryIterator($base);  		foreach($dirs as $dir) -			if(!$dir->isDot() && $dir->isDir()) +			if(!$dir->isDot() && $dir->isDir()  +				&& !preg_match("/\.svn/", $dir->getPathName()))  				$this->get_directories($dir->getPathName(), $groups);  	}  } diff --git a/tests/FunctionalTests/protected/application.xml b/tests/FunctionalTests/protected/application.xml index c4ea0be2..fe17e3c0 100644 --- a/tests/FunctionalTests/protected/application.xml +++ b/tests/FunctionalTests/protected/application.xml @@ -7,18 +7,12 @@  	</paths>
  	<modules>
 -		<module id="globalization" class="TGlobalization" Culture="zh_CN">
 -
 -			<!-- The translation type and source -->
 -			<translation 
 -				type="XLIFF" 
 -				source="Tests.messages" 
 -				autosave="true"
 -				cache="true"
 -				/>
 -			<!-- <translation type="gettext" source="I18N/messages" autosave="true" /> -->
 -			<!-- <translation type="SQLite" source="sqlite:///I18N/messages/sqlite_messages.db" autosave="true" /> -->
 -			<!-- <translation type="MySQL" source="mysql://root@localhost/i18n_example" autosave="true" /> -->
 +		<module id="globalization" class="TGlobalization">
 +			<translation  type="XLIFF" source="Tests.messages" 
 +					autosave="true" cache="true" />
 +		</module>
 +		<module id="logger" class="System.Log.TEventLog">
 +			<logger destination="file" directory="Tests.logs" />
  		</module>
  	</modules>
  </application>
\ No newline at end of file diff --git a/tests/FunctionalTests/protected/pages/I18N/BasicI18N.page b/tests/FunctionalTests/protected/pages/I18N/BasicI18N.page index 4e4a6d72..e09c2161 100644 --- a/tests/FunctionalTests/protected/pages/I18N/BasicI18N.page +++ b/tests/FunctionalTests/protected/pages/I18N/BasicI18N.page @@ -1,3 +1,4 @@ +<%@ Application.Globalization.Culture="zh_CN" %>
  <!DOCTYPE HTML PUBLIC 
  	"-//W3C//DTD XHTML 1.0 Strict//EN" 
  	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 @@ -35,7 +36,7 @@  <div>
  <com:TNumberFormat Value="10000" Type="currency"/>
  </div>
 -<div><com:TDateFormat /></div>
 +<div><com:TDateFormat Value="2006-01-15"/></div>
  <div>
  <com:TChoiceFormat Value="0">
 diff --git a/tests/FunctionalTests/protected/pages/I18N/BasicI18N.php b/tests/FunctionalTests/protected/pages/I18N/BasicI18N.php index 50d493d8..6920f439 100644 --- a/tests/FunctionalTests/protected/pages/I18N/BasicI18N.php +++ b/tests/FunctionalTests/protected/pages/I18N/BasicI18N.php @@ -24,11 +24,22 @@ class BasicI18N extends TPage   */
  class BasicI18NTestCase extends SeleniumTestCase
  {
 -	function testI18N()
 +	function setup()
  	{
  		$page = Prado::getApplication()->getTestPage(__FILE__);
  		$this->open($page);
  	}
 +
 +	function testI18N()
 +	{
 +		$this->verifyTitle("Basic I18N Test", "");
 +		$this->verifyTextPresent("Hello", "");
 +		$this->verifyTextPresent("US$10,000.00", "");
 +		$this->verifyTextPresent("2006年1月15日 上午12时00分00秒", "");
 +		$this->verifyTextPresent("None", "");
 +		$this->verifyTextPresent("One thing.", "");
 +		$this->verifyTextPresent("Many things.", "");
 +	}
  }
  ?>
\ No newline at end of file diff --git a/tests/FunctionalTests/protected/runtime/config.cache b/tests/FunctionalTests/protected/runtime/config.cache Binary files differindex 33317f88..890ff3da 100644 --- a/tests/FunctionalTests/protected/runtime/config.cache +++ b/tests/FunctionalTests/protected/runtime/config.cache diff --git a/tests/FunctionalTests/selenium/php/selenium.php b/tests/FunctionalTests/selenium/php/selenium.php index 33dc8801..5c24e57d 100644 --- a/tests/FunctionalTests/selenium/php/selenium.php +++ b/tests/FunctionalTests/selenium/php/selenium.php @@ -459,11 +459,6 @@ class SeleniumTestCase extends UnitTestCase  		parent::__construct();  	} -	public function initPage($file) -	{ -		$this->Page = prado::getApplication()->getTestPage($file); -	} -  	function __call($func, $args)  	{  		if(count($args) == 0)  | 
