[ Index ]
 

Code source de SPIP 1.9.2c

Accédez au Source d'autres logiciels libres

title

Body

[fermer]

/ecrire/safehtml/classes/ -> safehtml.php (source)

   1  <?php
   2  /**
   3   * SafeHTML Parser
   4   *
   5   * @package SafeHTML
   6   * @author  Roman Ivanov <thingol@mail.ru>
   7   * @copyright  2004-2005 Roman Ivanov
   8   * @license http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
   9   * @version    1.3.7
  10   * @link    http://pixel-apes.com/safehtml/
  11   */
  12  
  13  require_once (XML_HTMLSAX3 . 'HTMLSax3.php');
  14  
  15  class SafeHTML 
  16  {
  17   var $_xhtml = '';
  18   
  19   var $_counter = array();
  20   
  21   var $_stack = array();
  22   
  23   var $_dcCounter = array();
  24   
  25   var $_dcStack = array();
  26   
  27   var $_listScope = 0; 
  28   
  29   var $_liStack = array();
  30  
  31   var $_protoRegexps = array();
  32   
  33   var $_cssRegexps = array();
  34  
  35   var $singleTags = array('area', 'br', 'img', 'input', 'hr', 'wbr', );
  36  
  37   var $deleteTags = array(
  38    'applet', 'base',   'basefont', 'bgsound', 'blink',  'body', 
  39    'embed',  'frame',  'frameset', 'head', 'html',   'ilayer', 
  40    'iframe', 'layer',  'link',  'meta', 'object', 'style', 
  41    'title',  'script', 
  42    );
  43  
  44   var $deleteTagsContent = array('script', 'style', 'title', 'xml', );
  45  
  46   var $protocolFiltering = 'white';
  47  
  48   var $blackProtocols = array(
  49    'about',   'chrome',  'data',    'disk',  'hcp',  
  50    'help', 'javascript', 'livescript', 'lynxcgi',  'lynxexec', 
  51    'ms-help', 'ms-its',  'mhtml',   'mocha', 'opera',   
  52    'res',  'resource',   'shell',   'vbscript', 'view-source', 
  53    'vnd.ms.radio',    'wysiwyg', 
  54    );
  55  
  56   var $whiteProtocols = array(
  57    'ed2k',   'file', 'ftp',  'gopher', 'http',  'https', 
  58    'irc',    'mailto', 'news', 'nntp', 'telnet', 'webcal', 
  59    'xmpp', 'callto',
  60    );
  61  
  62   var $protocolAttributes = array(
  63    'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc', 'src', 
  64    );
  65  
  66   var $cssKeywords = array(
  67    'absolute', 'behavior',    'behaviour',   'content', 'expression', 
  68    'fixed', 'include-source', 'moz-binding',
  69    );
  70  
  71   var $noClose = array();
  72  
  73   var $closeParagraph = array(
  74    'address', 'blockquote', 'center', 'dd',   'dir',    'div', 
  75    'dl',   'dt',   'h1',  'h2',   'h3',  'h4', 
  76    'h5',   'h6',   'hr',  'isindex', 'listing',   'marquee', 
  77    'menu', 'multicol',   'ol',  'p',    'plaintext', 'pre', 
  78    'table',   'ul',   'xmp', 
  79    );
  80  
  81   var $tableTags = array(
  82    'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
  83    'thead',   'tr', 
  84    );
  85  
  86   var $listTags = array('dir', 'menu', 'ol', 'ul', 'dl', );
  87  
  88   var $attributes = array('dynsrc', 'id', 'name', );
  89  
  90   var $attributesNS = array('xml:lang', );
  91  
  92   function SafeHTML() 
  93   {
  94    //making regular expressions based on Proto & CSS arrays
  95    foreach ($this->blackProtocols as $proto) {
  96     $preg = "/[\s\x01-\x1F]*";
  97     for ($i=0; $i<strlen($proto); $i++) {
  98      $preg .= $proto{$i} . "[\s\x01-\x1F]*";
  99     }
 100     $preg .= ":/i";
 101     $this->_protoRegexps[] = $preg;
 102    }
 103  
 104    foreach ($this->cssKeywords as $css) {
 105     $this->_cssRegexps[] = '/' . $css . '/i';
 106    }
 107    return true;
 108   }
 109  
 110   function _writeAttrs ($attrs) 
 111   {
 112    if (is_array($attrs)) {
 113     foreach ($attrs as $name => $value) {
 114  
 115      $name = strtolower($name);
 116  
 117      if (strpos($name, 'on') === 0) {
 118       continue;
 119      }
 120      if (strpos($name, 'data') === 0) {
 121       continue;
 122      }
 123      if (in_array($name, $this->attributes)) {
 124       continue;
 125      }
 126      if (!preg_match("/^[a-z0-9-]+$/i", $name)) {
 127        if (!in_array($name, $this->attributesNS))
 128        {
 129         continue;
 130        }
 131      }
 132  
 133      if (($value === TRUE) || (is_null($value))) {
 134       $value = $name;
 135      }
 136  
 137      if ($name == 'style') {
 138                     
 139                     // removes insignificant backslahes
 140         $value = str_replace("\\", '', $value);
 141  
 142                     // removes CSS comments
 143                     while (1)
 144                     {
 145                       $_value = preg_replace("!/\*.*?\*/!s", '', $value);
 146                       if ($_value == $value) break;
 147                       $value = $_value;
 148                     }
 149                     
 150                     // replace all & to &amp;
 151         $value = str_replace('&amp;', '&', $value);
 152         $value = str_replace('&', '&amp;', $value);
 153  
 154         foreach ($this->_cssRegexps as $css) {
 155          if (preg_match($css, $value)) { 
 156           continue 2;
 157          }
 158         }
 159         foreach ($this->_protoRegexps as $proto) {
 160          if (preg_match($proto, $value)) {
 161           continue 2;
 162          }
 163         }
 164      }
 165  
 166      $tempval = preg_replace('/&#(\d+);?/me', "chr('\\1')", $value); //"'
 167      $tempval = preg_replace('/&#x([0-9a-f]+);?/mei', "chr(hexdec('\\1'))", $tempval);
 168  
 169      if ((in_array($name, $this->protocolAttributes)) && 
 170       (strpos($tempval, ':') !== false)) 
 171      {
 172       if ($this->protocolFiltering == 'black') {
 173        foreach ($this->_protoRegexps as $proto) {
 174         if (preg_match($proto, $tempval)) continue 2;
 175        }
 176       } else {
 177        $_tempval = explode(':', $tempval);
 178        $proto = $_tempval[0];
 179        if (!in_array($proto, $this->whiteProtocols)) {
 180         continue;
 181        }
 182       }
 183      }
 184  
 185      $value = str_replace("\"", "&quot;", $value);
 186      $this->_xhtml .= ' ' . $name . '="' . $value . '"';
 187     }
 188    }
 189    return true;
 190   }
 191  
 192   function _openHandler(&$parser, $name, $attrs) 
 193   {
 194    $name = strtolower($name);
 195  
 196    if (in_array($name, $this->deleteTagsContent)) {
 197     array_push($this->_dcStack, $name);
 198     $this->_dcCounter[$name] = isset($this->_dcCounter[$name]) ? $this->_dcCounter[$name]+1 : 1;
 199    }
 200    if (count($this->_dcStack) != 0) {
 201     return true;
 202    }
 203  
 204    if (in_array($name, $this->deleteTags)) {
 205     return true;
 206    }
 207    
 208    if (!preg_match("/^[a-z0-9]+$/i", $name)) {
 209     if (preg_match("!(?:\@|://)!i", $name)) {
 210      $this->_xhtml .= '&lt;' . $name . '&gt;';
 211     }
 212     return true;
 213    }
 214  
 215    if (in_array($name, $this->singleTags)) {
 216     $this->_xhtml .= '<' . $name;
 217     $this->_writeAttrs($attrs);
 218     $this->_xhtml .= ' />';
 219     return true;
 220    }
 221  
 222    // TABLES: cannot open table elements when we are not inside table
 223    if ((isset($this->_counter['table'])) && ($this->_counter['table'] <= 0) 
 224     && (in_array($name, $this->tableTags))) 
 225    {
 226     return true;
 227    }
 228  
 229    // PARAGRAPHS: close paragraph when closeParagraph tags opening
 230    if ((in_array($name, $this->closeParagraph)) && (in_array('p', $this->_stack))) {
 231     $this->_closeHandler($parser, 'p');
 232    }
 233  
 234    // LISTS: we should close <li> if <li> of the same level opening
 235    if ($name == 'li' && count($this->_liStack) && 
 236     $this->_listScope == $this->_liStack[count($this->_liStack)-1]) 
 237    {
 238     $this->_closeHandler($parser, 'li');
 239    }
 240  
 241    // LISTS: we want to know on what nesting level of lists we are
 242    if (in_array($name, $this->listTags)) {
 243     $this->_listScope++;
 244    }
 245    if ($name == 'li') {
 246     array_push($this->_liStack, $this->_listScope);
 247    }
 248     
 249    $this->_xhtml .= '<' . $name;
 250    $this->_writeAttrs($attrs);
 251    $this->_xhtml .= '>';
 252    array_push($this->_stack,$name);
 253    $this->_counter[$name] = isset($this->_counter[$name]) ? $this->_counter[$name]+1 : 1;
 254    return true;
 255   }
 256  
 257   function _closeHandler(&$parser, $name) 
 258   {
 259  
 260    $name = strtolower($name);
 261  
 262    if (isset($this->_dcCounter[$name]) && ($this->_dcCounter[$name] > 0) && 
 263     (in_array($name, $this->deleteTagsContent))) 
 264    {
 265       while ($name != ($tag = array_pop($this->_dcStack))) {
 266     $this->_dcCounter[$tag]--;
 267       }
 268  
 269       $this->_dcCounter[$name]--;
 270    }
 271  
 272    if (count($this->_dcStack) != 0) {
 273     return true;
 274    }
 275  
 276    if ((isset($this->_counter[$name])) && ($this->_counter[$name] > 0)) {
 277       while ($name != ($tag = array_pop($this->_stack))) {
 278        $this->_closeTag($tag);
 279       }
 280  
 281       $this->_closeTag($name);
 282    }
 283    return true;
 284   }
 285  
 286   function _closeTag($tag) 
 287   {
 288    if (!in_array($tag, $this->noClose)) {
 289     $this->_xhtml .= '</' . $tag . '>';
 290    }
 291  
 292    $this->_counter[$tag]--;
 293  
 294    if (in_array($tag, $this->listTags)) {
 295     $this->_listScope--;
 296    }
 297  
 298    if ($tag == 'li') {
 299     array_pop($this->_liStack);
 300    }
 301    return true;
 302   }
 303  
 304   function _dataHandler(&$parser, $data) 
 305   {
 306    if (count($this->_dcStack) == 0) {
 307     $this->_xhtml .= $data;
 308    }
 309    return true;
 310   }
 311  
 312   function _escapeHandler(&$parser, $data) 
 313   {
 314    return true;
 315   }
 316  
 317   function getXHTML () 
 318   {
 319    while ($tag = array_pop($this->_stack)) {
 320     $this->_closeTag($tag);
 321    }
 322    
 323    return $this->_xhtml;
 324   }
 325  
 326   function clear() 
 327   {
 328    $this->_xhtml = '';
 329    return true;
 330   }
 331  
 332   function parse($doc) 
 333   {
 334  
 335      // Save all '<' symbols
 336      $doc = preg_replace("/<(?=[^a-zA-Z\/\!\?\%])/", '&lt;', $doc);
 337  
 338      // Web documents shouldn't contains \x00 symbol
 339      $doc = str_replace("\x00", '', $doc);
 340  
 341      // Opera6 bug workaround
 342      $doc = str_replace("\xC0\xBC", '&lt;', $doc);
 343  
 344      // UTF-7 encoding ASCII decode
 345      $doc = $this->repackUTF7($doc);
 346  
 347      // Instantiate the parser
 348      $parser=& new XML_HTMLSax3();
 349  
 350      // Set up the parser
 351      $parser->set_object($this);
 352  
 353      $parser->set_element_handler('_openHandler','_closeHandler');
 354      $parser->set_data_handler('_dataHandler');
 355      $parser->set_escape_handler('_escapeHandler');
 356  
 357      $parser->parse($doc);
 358  
 359      return $this->getXHTML();
 360  
 361   }
 362  
 363      function repackUTF7($str)
 364      {
 365         return preg_replace_callback('!\+([0-9a-zA-Z/]+)\-!', array($this, 'repackUTF7Callback'), $str);
 366      }
 367  
 368      function repackUTF7Callback($str)
 369      {
 370         $str = base64_decode($str[1]);
 371         $str = preg_replace_callback('/^((?:\x00.)*)((?:[^\x00].)+)/', array($this, 'repackUTF7Back'), $str);
 372         return preg_replace('/\x00(.)/', '$1', $str);
 373      }
 374  
 375      function repackUTF7Back($str)
 376      {
 377         return $str[1].'+'.rtrim(base64_encode($str[2]), '=').'-';
 378      }
 379  }
 380  
 381  ?>


Généré le : Wed Nov 21 10:20:27 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics