b2evolution

Multilingual multiuser multiblog engine

b2evolution Technical Documentation (0.9.x) [ class tree: evocore ] [ index: evocore ] [ all elements ]

Source for file _class_htmlchecker.php

Documentation is available at _class_htmlchecker.php

  1. <?php
  2. /**
  3.  * SafeHtmlChecker
  4.  *
  5.  * checks HTML against a subset of elements to ensure safety and XHTML validation.
  6.  *
  7.  * b2evolution - {@link http://b2evolution.net/}
  8.  * Released under GNU GPL License - {@link http://b2evolution.net/about/license.html}
  9.  * @copyright (c)2003-2005 by Francois PLANQUE - {@link http://fplanque.net/}
  10.  *
  11.  * @package evocore
  12.  * @author Simon Willison, 23rd Feb 2003, modified by fplanque, sakichan
  13.  */
  14. if!defined('DB_USER') ) die'Please, do not access this page directly.' );
  15.  
  16. /**
  17.  * SafeHtmlChecker
  18.  *
  19.  * checks HTML against a subset of elements to ensure safety and XHTML validation.
  20.  *
  21.  * @package evocore
  22.  */
  23. {
  24.     var $tags;      // Array showing allowed attributes for tags
  25.     var $tagattrs;  // Array showing URI attributes
  26.     var $uri_attrs;
  27.     var $allowed_uri_scheme;
  28.  
  29.     // Internal variables
  30.     var $parser;
  31.     var $stack = array();
  32.     var $last_checked_pos;
  33.     var $error;
  34.  
  35.     /**
  36.      * Constructor
  37.      *
  38.      * {@internal This gets tested in _libs.misc.simpletest.php}}
  39.      *
  40.      * @param array 
  41.      * @param array 
  42.      * @param array 
  43.      * @param array 
  44.      * @param string Input encoding to use ('ISO-8859-1', 'UTF-8', 'US-ASCII' or '' for auto-detect)
  45.      */
  46.     function SafeHtmlChecker$allowed_tags$allowed_attributes$uri_attrs$allowed_uri_scheme$encoding '' )
  47.     {
  48.         $this->tags = $allowed_tags;
  49.         $this->tagattrs = $allowed_attributes;
  50.         $this->uri_attrs = $uri_attrs;
  51.         $this->allowed_uri_scheme = $allowed_uri_scheme;
  52.  
  53.  
  54.         $encoding strtoupper($encoding)// we might get 'iso-8859-1' for example
  55.         $this->encoding $encoding;
  56.         ifin_array$encodingarray'ISO-8859-1''UTF-8''US-ASCII' ) ) )
  57.         // passed encoding not supported by xml_parser_create()
  58.             $this->xml_parser_encoding ''// auto-detect (in PHP4, in PHP5 anyway)
  59.         }
  60.         else
  61.         {
  62.             $this->xml_parser_encoding $this->encoding;
  63.         }
  64.         $this->parser = xml_parser_create$this->xml_parser_encoding );
  65.  
  66.         $this->last_checked_pos = 0;
  67.         $this->error = false;
  68.  
  69.         // Creates the parser
  70.         xml_set_object$this->parser$this);
  71.  
  72.         // set functions to call when a start or end tag is encountered
  73.         xml_set_element_handler($this->parser'tag_open''tag_close');
  74.         // set function to call for the actual data
  75.         xml_set_character_data_handler($this->parser'cdata');
  76.  
  77.         xml_set_default_handler($this->parser'default_handler');
  78.         xml_set_external_entity_ref_handler($this->parser'external_entity');
  79.         xml_set_unparsed_entity_decl_handler($this->parser'unparsed_entity');
  80.  
  81.         xml_parser_set_option($this->parserXML_OPTION_CASE_FOLDINGfalse);
  82.     }
  83.  
  84.     function default_handler$parser$data)
  85.     {
  86.         // echo 'default handler: '.$data.'<br />';
  87.     }
  88.  
  89.     function external_entity$parser$open_entity_names$base$system_id$public_id)
  90.     {
  91.         // echo 'external_entity<br />';
  92.     }
  93.  
  94.  
  95.     function unparsed_entity$parser$entity_name$base$system_id$public_id$notation_name)
  96.     {
  97.         // echo 'unparsed_entity<br />';
  98.     }
  99.  
  100.  
  101.     /**
  102.      * check(-)
  103.      */
  104.     function check($xhtml)
  105.     {
  106.         // Convert encoding:
  107.         ifempty($this->xml_parser_encoding|| $this->encoding != $this->xml_parser_encoding )
  108.         // we need to convert encoding:
  109.             iffunction_exists'mb_convert_encoding' ) )
  110.             // we can convert encoding to UTF-8
  111.                 $this->encoding 'UTF-8';
  112.  
  113.                 // Convert XHTML:
  114.                 $xhtml mb_convert_encoding$xhtml'UTF-8' );
  115.             }
  116.         }
  117.  
  118.         // Open comments or '<![CDATA[' are dangerous
  119.         $xhtml str_replace('<!'''$xhtml);
  120.  
  121.         // Convert isolated & chars
  122.         $xhtml preg_replace'#(\s)&(\s)#''\\1&amp;\\2'$xhtml );
  123.  
  124.         $xhtml_head '<?xml version="1.0"';
  125.         ifempty($this->encoding) )
  126.         {
  127.             $xhtml_head .= ' encoding="'.$this->encoding.'"';
  128.         }
  129.         $xhtml_head .= '?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
  130.  
  131.         $xhtml $xhtml_head.'<body>'.$xhtml.'</body>';
  132.  
  133.         if!xml_parse($this->parser$xhtml) )
  134.         {
  135.             $xml_error_code xml_get_error_code$this->parser );
  136.             $xml_error_string xml_error_string$xml_error_code );
  137.             switch$xml_error_code )
  138.             {
  139.                 case XML_ERROR_TAG_MISMATCH:
  140.                     $xml_error_string .= ': <code>'.$this->stack[count($this->stack)-1].'</code>';
  141.                     break;
  142.             }
  143.             $pos xml_get_current_byte_index($this->parser);
  144.             $xml_error_string .= ' near <code>'.htmlspecialcharssubstr$xhtml$this->last_checked_pos$pos-$this->last_checked_pos+20 ) ).'</code>';
  145.  
  146.             $this->html_errorT_('Parser error: ').$xml_error_string );
  147.         }
  148.     }
  149.  
  150.     /**
  151.      * tag_open(-)
  152.      *
  153.      * Called when the parser finds an opening tag
  154.      */
  155.     function tag_open($parser$tag$attrs)
  156.     {
  157.         // echo "processing tag: $tag <br />\n";
  158.         $this->last_checked_pos = xml_get_current_byte_index($this->parser);
  159.  
  160.         if ($tag == 'body')
  161.         {
  162.             ifcount($this->stack)
  163.                 $this->html_errorT_('Tag <code>body</code> can only be used once!') );
  164.             $this->stack[$tag;
  165.             return;
  166.         }
  167.         $previous $this->stack[count($this->stack)-1];
  168.  
  169.         // If previous tag is illegal, no point in running tests
  170.         if (!in_array($previousarray_keys($this->tags))) {
  171.             $this->stack[$tag;
  172.             return;
  173.         }
  174.         // Is tag a legal tag?
  175.         if (!in_array($tagarray_keys($this->tags))) {
  176.             $this->html_errorT_('Illegal tag')": <code>$tag</code>);
  177.             $this->stack[$tag;
  178.             return;
  179.         }
  180.         // Is tag allowed in the current context?
  181.         if (!in_array($tagexplode(' '$this->tags[$previous]))) {
  182.             if ($previous == 'body'{
  183.                 $this->html_error(    sprintfT_('Tag %s must occur inside another tag')'<code>'.$tag.'</code>' ) );
  184.             else {
  185.                 $this->html_error(    sprintfT_('Tag %s is not allowed within tag %s')'<code>'.$tag.'</code>''<code>'.$previous.'</code>') );
  186.             }
  187.         }
  188.         // Are tag attributes valid?
  189.         foreach$attrs as $attr => $value )
  190.         {
  191.             if (!isset($this->tagattrs[$tag]|| !in_array($attrexplode(' '$this->tagattrs[$tag])))
  192.             {
  193.                 $this->html_errorsprintfT_('Tag %s may not have attribute %s')'<code>'.$tag.'</code>''<code>'.$attr.'</code>' ) );
  194.             }
  195.             if (in_array($attr$this->uri_attrs))
  196.             // Must this attribute be checked for URIs
  197.                 $matches array();
  198.                 $value trim($value);
  199.                 if$error validate_url$value$this->allowed_uri_scheme ) )
  200.                 {
  201.                     $this->html_errorT_('Found invalid URL: ').$error );
  202.                 }
  203.             }
  204.         }
  205.         // Set previous, used for checking nesting context rules
  206.         $this->stack[$tag;
  207.     }
  208.  
  209.     /**
  210.      * cdata(-)
  211.      */
  212.     function cdata($parser$cdata)
  213.     {
  214.         $this->last_checked_pos = xml_get_current_byte_index($this->parser);
  215.  
  216.         // Simply check that the 'previous' tag allows CDATA
  217.         $previous $this->stack[count($this->stack)-1];
  218.         // If previous tag is illegal, no point in running test
  219.         if (!in_array($previousarray_keys($this->tags))) {
  220.             return;
  221.         }
  222.         if (trim($cdata!= ''{
  223.             if (!in_array('#PCDATA'explode(' '$this->tags[$previous]))) {
  224.                 $this->html_error(    sprintfT_('Tag %s may not contain raw character data')'<code>'.$previous.'</code>' ) );
  225.             }
  226.         }
  227.     }
  228.  
  229.     /**
  230.      * tag_close(-)
  231.      */
  232.     function tag_close($parser$tag)
  233.     {
  234.         $this->last_checked_pos = xml_get_current_byte_index($this->parser);
  235.  
  236.         // Move back one up the stack
  237.         array_pop($this->stack);
  238.     }
  239.  
  240.     function html_error$string )
  241.     {
  242.         $this->error = true;
  243.         errors_add$string );
  244.     }
  245.  
  246.     /**
  247.      * isOK(-)
  248.      */
  249.     function isOK()
  250.     {
  251.         return $this->error;
  252.     }
  253.  
  254. }
  255.  
  256. ?>

Documentation generated on Tue, 20 May 2008 01:53:02 +0200 by phpDocumentor 1.4.2