| [ Index ] |
|
Code source de SPIP 1.9.2c |
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 & 151 $value = str_replace('&', '&', $value); 152 $value = str_replace('&', '&', $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("\"", """, $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 .= '<' . $name . '>'; 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\/\!\?\%])/", '<', $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", '<', $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 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Wed Nov 21 10:20:27 2007 | par Balluche grâce à PHPXref 0.7 |
|