PHP Cross Reference of WordPress Subversion HEAD

[ Index ]     [ Classes ]     [ Functions ]     [ Variables ]     [ Constants ]

title

Body

[close]

/wp-includes/ -> kses.php (source)

   1  <?php
   2  
   3  // Added wp_ prefix to avoid conflicts with existing kses users
   4  # kses 0.2.2 - HTML/XHTML filter that only allows some elements and attributes
   5  # Copyright (C) 2002, 2003, 2005  Ulf Harnhammar
   6  # *** CONTACT INFORMATION ***
   7  #
   8  # E-mail:      metaur at users dot sourceforge dot net
   9  # Web page:    http://sourceforge.net/projects/kses
  10  # Paper mail:  Ulf Harnhammar
  11  #              Ymergatan 17 C
  12  #              753 25  Uppsala
  13  #              SWEDEN
  14  #
  15  # [kses strips evil scripts!]
  16  if (!defined('CUSTOM_TAGS'))
  17      define('CUSTOM_TAGS', false);
  18  
  19  // You can override this in your my-hacks.php file
  20  if (!CUSTOM_TAGS) {
  21      $allowedposttags = array(
  22          'address' => array(),
  23          'a' => array(
  24              'href' => array(), 'title' => array(),
  25              'rel' => array(), 'rev' => array(),
  26              'name' => array()
  27              ),
  28          'abbr' => array(
  29              'title' => array(), 'class' => array()
  30              ),
  31          'acronym' => array(
  32              'title' => array()
  33              ),
  34          'b' => array(),
  35          'big' => array(),
  36          'blockquote' => array(
  37              'cite' => array(), 'xml:lang' => array(),
  38              'lang' => array()
  39              ),
  40          'br' => array(),
  41          'button' => array(
  42              'disabled' => array(), 'name' => array(),
  43              'type' => array(), 'value' => array()
  44              ),
  45          'caption' => array(
  46              'align' => array()
  47              ),
  48          'code' => array(),
  49          'col' => array(
  50              'align' => array(), 'char' => array(),
  51              'charoff' => array(), 'span' => array(),
  52              'valign' => array(), 'width' => array()
  53              ),
  54          'del' => array(
  55              'datetime' => array()
  56              ),
  57          'dd' => array(),
  58          'div' => array(
  59              'align' => array(), 'xml:lang' => array(),
  60              'lang' => array()
  61              ),
  62          'dl' => array(),
  63          'dt' => array(),
  64          'em' => array(),
  65          'fieldset' => array(),
  66          'font' => array(
  67              'color' => array(), 'face' => array(),
  68              'size' => array()
  69              ),
  70          'form' => array(
  71              'action' => array(), 'accept' => array(),
  72              'accept-charset' => array(), 'enctype' => array(),
  73              'method' => array(), 'name' => array(),
  74              'target' => array()
  75              ),
  76          'h1' => array(
  77              'align' => array()
  78              ),
  79          'h2' => array(
  80              'align' => array()
  81              ),
  82          'h3' => array(
  83              'align' => array()
  84              ),
  85          'h4' => array(
  86              'align' => array()
  87              ),
  88          'h5' => array(
  89              'align' => array()
  90              ),
  91          'h6' => array(
  92              'align' => array()
  93              ),
  94          'hr' => array(
  95              'align' => array(), 'noshade' => array(),
  96              'size' => array(), 'width' => array()
  97              ),
  98          'i' => array(),
  99          'img' => array(
 100              'alt' => array(), 'align' => array(),
 101              'border' => array(), 'height' => array(),
 102              'hspace' => array(), 'longdesc' => array(),
 103              'vspace' => array(), 'src' => array(),
 104              'width' => array()
 105              ),
 106          'ins' => array(
 107              'datetime' => array(), 'cite' => array()
 108              ),
 109          'kbd' => array(),
 110          'label' => array(
 111              'for' => array()
 112              ),
 113          'legend' => array(
 114              'align' => array()
 115              ),
 116          'li' => array(),
 117          'p' => array(
 118              'align' => array(), 'xml:lang' => array(),
 119              'lang' => array()
 120              ),
 121          'pre' => array(
 122              'width' => array()
 123              ),
 124          'q' => array(
 125              'cite' => array()
 126              ),
 127          's' => array(),
 128          'strike' => array(),
 129          'strong' => array(),
 130          'sub' => array(),
 131          'sup' => array(),
 132          'table' => array(
 133              'align' => array(), 'bgcolor' => array(),
 134              'border' => array(), 'cellpadding' => array(),
 135              'cellspacing' => array(), 'rules' => array(),
 136              'summary' => array(), 'width' => array()
 137              ),
 138          'tbody' => array(
 139              'align' => array(), 'char' => array(),
 140              'charoff' => array(), 'valign' => array()
 141              ),
 142          'td' => array(
 143              'abbr' => array(), 'align' => array(),
 144              'axis' => array(), 'bgcolor' => array(),
 145              'char' => array(), 'charoff' => array(),
 146              'colspan' => array(), 'headers' => array(),
 147              'height' => array(), 'nowrap' => array(),
 148              'rowspan' => array(), 'scope' => array(),
 149              'valign' => array(), 'width' => array()
 150              ),
 151          'textarea' => array(
 152              'cols' => array(), 'rows' => array(),
 153              'disabled' => array(), 'name' => array(),
 154              'readonly' => array()
 155              ),
 156          'tfoot' => array(
 157              'align' => array(), 'char' => array(),
 158              'charoff' => array(), 'valign' => array()
 159              ),
 160          'th' => array(
 161              'abbr' => array(), 'align' => array(),
 162              'axis' => array(), 'bgcolor' => array(),
 163              'char' => array(), 'charoff' => array(),
 164              'colspan' => array(), 'headers' => array(),
 165              'height' => array(), 'nowrap' => array(),
 166              'rowspan' => array(), 'scope' => array(),
 167              'valign' => array(), 'width' => array()
 168              ),
 169          'thead' => array(
 170              'align' => array(), 'char' => array(),
 171              'charoff' => array(), 'valign' => array()
 172              ),
 173          'title' => array(),
 174          'tr' => array(
 175              'align' => array(), 'bgcolor' => array(),
 176              'char' => array(), 'charoff' => array(),
 177              'valign' => array()
 178              ),
 179          'tt' => array(),
 180          'u' => array(),
 181          'ul' => array(),
 182          'ol' => array(),
 183          'var' => array()
 184      );
 185  
 186      $allowedtags = array(
 187          'a' => array(
 188              'href' => array(), 'title' => array()
 189              ),
 190          'abbr' => array(
 191              'title' => array()
 192              ),
 193          'acronym' => array(
 194              'title' => array()
 195              ),
 196          'b' => array(),
 197          'blockquote' => array(
 198              'cite' => array()
 199              ),
 200          //    'br' => array(),
 201          'code' => array(),
 202          //    'del' => array('datetime' => array()),
 203          //    'dd' => array(),
 204          //    'dl' => array(),
 205          //    'dt' => array(),
 206          'em' => array(),
 207          'i' => array(),
 208          //    'ins' => array('datetime' => array(), 'cite' => array()),
 209          //    'li' => array(),
 210          //    'ol' => array(),
 211          //    'p' => array(),
 212          //    'q' => array(),
 213          'strike' => array(),
 214          'strong' => array(),
 215          //    'sub' => array(),
 216          //    'sup' => array(),
 217          //    'u' => array(),
 218          //    'ul' => array(),
 219      );
 220  }
 221  
 222  function wp_kses($string, $allowed_html, $allowed_protocols = array ('http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet'))
 223      ###############################################################################
 224          # This function makes sure that only the allowed HTML element names, attribute
 225          # names and attribute values plus only sane HTML entities will occur in
 226          # $string. You have to remove any slashes from PHP's magic quotes before you
 227          # call this function.
 228          ###############################################################################
 229      {
 230      $string = wp_kses_no_null($string);
 231      $string = wp_kses_js_entities($string);
 232      $string = wp_kses_normalize_entities($string);
 233      $allowed_html_fixed = wp_kses_array_lc($allowed_html);
 234      $string = wp_kses_hook($string, $allowed_html_fixed, $allowed_protocols); // WP changed the order of these funcs and added args to wp_kses_hook
 235      return wp_kses_split($string, $allowed_html_fixed, $allowed_protocols);
 236  } # function wp_kses
 237  
 238  function wp_kses_hook($string, $allowed_html, $allowed_protocols)
 239  ###############################################################################
 240  # You add any kses hooks here.
 241  ###############################################################################
 242  {
 243      $string = apply_filters('pre_kses', $string, $allowed_html, $allowed_protocols);
 244      return $string;
 245  } # function wp_kses_hook
 246  
 247  function wp_kses_version()
 248  ###############################################################################
 249  # This function returns kses' version number.
 250  ###############################################################################
 251  {
 252      return '0.2.2';
 253  } # function wp_kses_version
 254  
 255  function wp_kses_split($string, $allowed_html, $allowed_protocols)
 256  ###############################################################################
 257  # This function searches for HTML tags, no matter how malformed. It also
 258  # matches stray ">" characters.
 259  ###############################################################################
 260  {
 261      return preg_replace('%((<!--.*?(-->|$))|(<[^>]*(>|$)|>))%e',
 262      "wp_kses_split2('\\1', \$allowed_html, ".'$allowed_protocols)', $string);
 263  } # function wp_kses_split
 264  
 265  function wp_kses_split2($string, $allowed_html, $allowed_protocols)
 266  ###############################################################################
 267  # This function does a lot of work. It rejects some very malformed things
 268  # like <:::>. It returns an empty string, if the element isn't allowed (look
 269  # ma, no strip_tags()!). Otherwise it splits the tag into an element and an
 270  # attribute list.
 271  ###############################################################################
 272  {
 273      $string = wp_kses_stripslashes($string);
 274  
 275      if (substr($string, 0, 1) != '<')
 276          return '&gt;';
 277      # It matched a ">" character
 278  
 279      if (preg_match('%^<!--(.*?)(-->)?$%', $string, $matches)) {
 280          $string = str_replace(array('<!--', '-->'), '', $matches[1]);
 281          while ( $string != $newstring = wp_kses($string, $allowed_html, $allowed_protocols) )
 282              $string = $newstring;
 283          if ( $string == '' )
 284              return '';
 285          return "<!--{$string}-->";
 286      }
 287      # Allow HTML comments
 288  
 289      if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string, $matches))
 290          return '';
 291      # It's seriously malformed
 292  
 293      $slash = trim($matches[1]);
 294      $elem = $matches[2];
 295      $attrlist = $matches[3];
 296  
 297      if (!@isset($allowed_html[strtolower($elem)]))
 298          return '';
 299      # They are using a not allowed HTML element
 300  
 301      if ($slash != '')
 302          return "<$slash$elem>";
 303      # No attributes are allowed for closing elements
 304  
 305      return wp_kses_attr("$slash$elem", $attrlist, $allowed_html, $allowed_protocols);
 306  } # function wp_kses_split2
 307  
 308  function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols)
 309  ###############################################################################
 310  # This function removes all attributes, if none are allowed for this element.
 311  # If some are allowed it calls wp_kses_hair() to split them further, and then it
 312  # builds up new HTML code from the data that kses_hair() returns. It also
 313  # removes "<" and ">" characters, if there are any left. One more thing it
 314  # does is to check if the tag has a closing XHTML slash, and if it does,
 315  # it puts one in the returned code as well.
 316  ###############################################################################
 317  {
 318      # Is there a closing XHTML slash at the end of the attributes?
 319  
 320      $xhtml_slash = '';
 321      if (preg_match('%\s/\s*$%', $attr))
 322          $xhtml_slash = ' /';
 323  
 324      # Are any attributes allowed at all for this element?
 325  
 326      if (@ count($allowed_html[strtolower($element)]) == 0)
 327          return "<$element$xhtml_slash>";
 328  
 329      # Split it
 330  
 331      $attrarr = wp_kses_hair($attr, $allowed_protocols);
 332  
 333      # Go through $attrarr, and save the allowed attributes for this element
 334      # in $attr2
 335  
 336      $attr2 = '';
 337  
 338      foreach ($attrarr as $arreach) {
 339          if (!@ isset ($allowed_html[strtolower($element)][strtolower($arreach['name'])]))
 340              continue; # the attribute is not allowed
 341  
 342          $current = $allowed_html[strtolower($element)][strtolower($arreach['name'])];
 343          if ($current == '')
 344              continue; # the attribute is not allowed
 345  
 346          if (!is_array($current))
 347              $attr2 .= ' '.$arreach['whole'];
 348          # there are no checks
 349  
 350          else {
 351              # there are some checks
 352              $ok = true;
 353              foreach ($current as $currkey => $currval)
 354                  if (!wp_kses_check_attr_val($arreach['value'], $arreach['vless'], $currkey, $currval)) {
 355                      $ok = false;
 356                      break;
 357                  }
 358  
 359              if ($ok)
 360                  $attr2 .= ' '.$arreach['whole']; # it passed them
 361          } # if !is_array($current)
 362      } # foreach
 363  
 364      # Remove any "<" or ">" characters
 365  
 366      $attr2 = preg_replace('/[<>]/', '', $attr2);
 367  
 368      return "<$element$attr2$xhtml_slash>";
 369  } # function wp_kses_attr
 370  
 371  function wp_kses_hair($attr, $allowed_protocols)
 372  ###############################################################################
 373  # This function does a lot of work. It parses an attribute list into an array
 374  # with attribute data, and tries to do the right thing even if it gets weird
 375  # input. It will add quotes around attribute values that don't have any quotes
 376  # or apostrophes around them, to make it easier to produce HTML code that will
 377  # conform to W3C's HTML specification. It will also remove bad URL protocols
 378  # from attribute values.
 379  ###############################################################################
 380  {
 381      $attrarr = array ();
 382      $mode = 0;
 383      $attrname = '';
 384  
 385      # Loop through the whole attribute list
 386  
 387      while (strlen($attr) != 0) {
 388          $working = 0; # Was the last operation successful?
 389  
 390          switch ($mode) {
 391              case 0 : # attribute name, href for instance
 392  
 393                  if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
 394                      $attrname = $match[1];
 395                      $working = $mode = 1;
 396                      $attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
 397                  }
 398  
 399                  break;
 400  
 401              case 1 : # equals sign or valueless ("selected")
 402  
 403                  if (preg_match('/^\s*=\s*/', $attr)) # equals sign
 404                      {
 405                      $working = 1;
 406                      $mode = 2;
 407                      $attr = preg_replace('/^\s*=\s*/', '', $attr);
 408                      break;
 409                  }
 410  
 411                  if (preg_match('/^\s+/', $attr)) # valueless
 412                      {
 413                      $working = 1;
 414                      $mode = 0;
 415                      $attrarr[] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
 416                      $attr = preg_replace('/^\s+/', '', $attr);
 417                  }
 418  
 419                  break;
 420  
 421              case 2 : # attribute value, a URL after href= for instance
 422  
 423                  if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match))
 424                      # "value"
 425                      {
 426                      $thisval = wp_kses_bad_protocol($match[1], $allowed_protocols);
 427  
 428                      $attrarr[] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
 429                      $working = 1;
 430                      $mode = 0;
 431                      $attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
 432                      break;
 433                  }
 434  
 435                  if (preg_match("/^'([^']*)'(\s+|$)/", $attr, $match))
 436                      # 'value'
 437                      {
 438                      $thisval = wp_kses_bad_protocol($match[1], $allowed_protocols);
 439  
 440                      $attrarr[] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname='$thisval'", 'vless' => 'n');
 441                      $working = 1;
 442                      $mode = 0;
 443                      $attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
 444                      break;
 445                  }
 446  
 447                  if (preg_match("%^([^\s\"']+)(\s+|$)%", $attr, $match))
 448                      # value
 449                      {
 450                      $thisval = wp_kses_bad_protocol($match[1], $allowed_protocols);
 451  
 452                      $attrarr[] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
 453                      # We add quotes to conform to W3C's HTML spec.
 454                      $working = 1;
 455                      $mode = 0;
 456                      $attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
 457                  }
 458  
 459                  break;
 460          } # switch
 461  
 462          if ($working == 0) # not well formed, remove and try again
 463              {
 464              $attr = wp_kses_html_error($attr);
 465              $mode = 0;
 466          }
 467      } # while
 468  
 469      if ($mode == 1)
 470          # special case, for when the attribute list ends with a valueless
 471          # attribute like "selected"
 472          $attrarr[] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
 473  
 474      return $attrarr;
 475  } # function wp_kses_hair
 476  
 477  function wp_kses_check_attr_val($value, $vless, $checkname, $checkvalue)
 478  ###############################################################################
 479  # This function performs different checks for attribute values. The currently
 480  # implemented checks are "maxlen", "minlen", "maxval", "minval" and "valueless"
 481  # with even more checks to come soon.
 482  ###############################################################################
 483  {
 484      $ok = true;
 485  
 486      switch (strtolower($checkname)) {
 487          case 'maxlen' :
 488              # The maxlen check makes sure that the attribute value has a length not
 489              # greater than the given value. This can be used to avoid Buffer Overflows
 490              # in WWW clients and various Internet servers.
 491  
 492              if (strlen($value) > $checkvalue)
 493                  $ok = false;
 494              break;
 495  
 496          case 'minlen' :
 497              # The minlen check makes sure that the attribute value has a length not
 498              # smaller than the given value.
 499  
 500              if (strlen($value) < $checkvalue)
 501                  $ok = false;
 502              break;
 503  
 504          case 'maxval' :
 505              # The maxval check does two things: it checks that the attribute value is
 506              # an integer from 0 and up, without an excessive amount of zeroes or
 507              # whitespace (to avoid Buffer Overflows). It also checks that the attribute
 508              # value is not greater than the given value.
 509              # This check can be used to avoid Denial of Service attacks.
 510  
 511              if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
 512                  $ok = false;
 513              if ($value > $checkvalue)
 514                  $ok = false;
 515              break;
 516  
 517          case 'minval' :
 518              # The minval check checks that the attribute value is a positive integer,
 519              # and that it is not smaller than the given value.
 520  
 521              if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
 522                  $ok = false;
 523              if ($value < $checkvalue)
 524                  $ok = false;
 525              break;
 526  
 527          case 'valueless' :
 528              # The valueless check checks if the attribute has a value
 529              # (like <a href="blah">) or not (<option selected>). If the given value
 530              # is a "y" or a "Y", the attribute must not have a value.
 531              # If the given value is an "n" or an "N", the attribute must have one.
 532  
 533              if (strtolower($checkvalue) != $vless)
 534                  $ok = false;
 535              break;
 536      } # switch
 537  
 538      return $ok;
 539  } # function wp_kses_check_attr_val
 540  
 541  function wp_kses_bad_protocol($string, $allowed_protocols)
 542  ###############################################################################
 543  # This function removes all non-allowed protocols from the beginning of
 544  # $string. It ignores whitespace and the case of the letters, and it does
 545  # understand HTML entities. It does its work in a while loop, so it won't be
 546  # fooled by a string like "javascript:javascript:alert(57)".
 547  ###############################################################################
 548  {
 549      $string = wp_kses_no_null($string);
 550      $string = preg_replace('/\xad+/', '', $string); # deals with Opera "feature"
 551      $string2 = $string.'a';
 552  
 553      while ($string != $string2) {
 554          $string2 = $string;
 555          $string = wp_kses_bad_protocol_once($string, $allowed_protocols);
 556      } # while
 557  
 558      return $string;
 559  } # function wp_kses_bad_protocol
 560  
 561  function wp_kses_no_null($string)
 562  ###############################################################################
 563  # This function removes any NULL characters in $string.
 564  ###############################################################################
 565  {
 566      $string = preg_replace('/\0+/', '', $string);
 567      $string = preg_replace('/(\\\\0)+/', '', $string);
 568  
 569      return $string;
 570  } # function wp_kses_no_null
 571  
 572  function wp_kses_stripslashes($string)
 573  ###############################################################################
 574  # This function changes the character sequence  \"  to just  "
 575  # It leaves all other slashes alone. It's really weird, but the quoting from
 576  # preg_replace(//e) seems to require this.
 577  ###############################################################################
 578  {
 579      return preg_replace('%\\\\"%', '"', $string);
 580  } # function wp_kses_stripslashes
 581  
 582  function wp_kses_array_lc($inarray)
 583  ###############################################################################
 584  # This function goes through an array, and changes the keys to all lower case.
 585  ###############################################################################
 586  {
 587      $outarray = array ();
 588  
 589      foreach ($inarray as $inkey => $inval) {
 590          $outkey = strtolower($inkey);
 591          $outarray[$outkey] = array ();
 592  
 593          foreach ($inval as $inkey2 => $inval2) {
 594              $outkey2 = strtolower($inkey2);
 595              $outarray[$outkey][$outkey2] = $inval2;
 596          } # foreach $inval
 597      } # foreach $inarray
 598  
 599      return $outarray;
 600  } # function wp_kses_array_lc
 601  
 602  function wp_kses_js_entities($string)
 603  ###############################################################################
 604  # This function removes the HTML JavaScript entities found in early versions of
 605  # Netscape 4.
 606  ###############################################################################
 607  {
 608      return preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
 609  } # function wp_kses_js_entities
 610  
 611  function wp_kses_html_error($string)
 612  ###############################################################################
 613  # This function deals with parsing errors in wp_kses_hair(). The general plan is
 614  # to remove everything to and including some whitespace, but it deals with
 615  # quotes and apostrophes as well.
 616  ###############################################################################
 617  {
 618      return preg_replace('/^("[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*/', '', $string);
 619  } # function wp_kses_html_error
 620  
 621  function wp_kses_bad_protocol_once($string, $allowed_protocols)
 622  ###############################################################################
 623  # This function searches for URL protocols at the beginning of $string, while
 624  # handling whitespace and HTML entities.
 625  ###############################################################################
 626  {
 627      return preg_replace('/^((&[^;]*;|[\sA-Za-z0-9])*)'.'(:|&#58;|&#[Xx]3[Aa];)\s*/e', 'wp_kses_bad_protocol_once2("\\1", $allowed_protocols)', $string);
 628  } # function wp_kses_bad_protocol_once
 629  
 630  function wp_kses_bad_protocol_once2($string, $allowed_protocols)
 631  ###############################################################################
 632  # This function processes URL protocols, checks to see if they're in the white-
 633  # list or not, and returns different data depending on the answer.
 634  ###############################################################################
 635  {
 636      $string2 = wp_kses_decode_entities($string);
 637      $string2 = preg_replace('/\s/', '', $string2);
 638      $string2 = wp_kses_no_null($string2);
 639      $string2 = preg_replace('/\xad+/', '', $string2);
 640      # deals with Opera "feature"
 641      $string2 = strtolower($string2);
 642  
 643      $allowed = false;
 644      foreach ($allowed_protocols as $one_protocol)
 645          if (strtolower($one_protocol) == $string2) {
 646              $allowed = true;
 647              break;
 648          }
 649  
 650      if ($allowed)
 651          return "$string2:";
 652      else
 653          return '';
 654  } # function wp_kses_bad_protocol_once2
 655  
 656  function wp_kses_normalize_entities($string)
 657  ###############################################################################
 658  # This function normalizes HTML entities. It will convert "AT&T" to the correct
 659  # "AT&amp;T", "&#00058;" to "&#58;", "&#XYZZY;" to "&amp;#XYZZY;" and so on.
 660  ###############################################################################
 661  {
 662      # Disarm all entities by converting & to &amp;
 663  
 664      $string = str_replace('&', '&amp;', $string);
 665  
 666      # Change back the allowed entities in our entity whitelist
 667  
 668      $string = preg_replace('/&amp;([A-Za-z][A-Za-z0-9]{0,19});/', '&\\1;', $string);
 669      $string = preg_replace('/&amp;#0*([0-9]{1,5});/e', 'wp_kses_normalize_entities2("\\1")', $string);
 670      $string = preg_replace('/&amp;#([Xx])0*(([0-9A-Fa-f]{2}){1,2});/', '&#\\1\\2;', $string);
 671  
 672      return $string;
 673  } # function wp_kses_normalize_entities
 674  
 675  function wp_kses_normalize_entities2($i)
 676  ###############################################################################
 677  # This function helps wp_kses_normalize_entities() to only accept 16 bit values
 678  # and nothing more for &#number; entities.
 679  ###############################################################################
 680  {
 681      return (($i > 65535) ? "&amp;#$i;" : "&#$i;");
 682  } # function wp_kses_normalize_entities2
 683