[ Root ] [ Search ] [ Index ]

PHP Cross Reference of WordPress MU 2.9.2

Provided by Yoast

title

Body

[close]

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

   1  <?php
   2  /**
   3   * HTML/XHTML filter that only allows some elements and attributes
   4   *
   5   * Added wp_ prefix to avoid conflicts with existing kses users
   6   *
   7   * @version 0.2.2
   8   * @copyright (C) 2002, 2003, 2005
   9   * @author Ulf Harnhammar <metaur@users.sourceforge.net>
  10   *
  11   * @package External
  12   * @subpackage KSES
  13   *
  14   * @internal
  15   * *** CONTACT INFORMATION ***
  16   * E-mail:      metaur at users dot sourceforge dot net
  17   * Web page:    http://sourceforge.net/projects/kses
  18   * Paper mail:  Ulf Harnhammar
  19   *              Ymergatan 17 C
  20   *              753 25  Uppsala
  21   *              SWEDEN
  22   *
  23   * [kses strips evil scripts!]
  24   */
  25  
  26  /**
  27   * You can override this in your my-hacks.php file You can also override this
  28   * in a plugin file. The my-hacks.php is deprecated in its usage.
  29   *
  30   * @since 1.2.0
  31   */
  32  if (!defined('CUSTOM_TAGS'))
  33      define('CUSTOM_TAGS', false);
  34  
  35  if (!CUSTOM_TAGS) {
  36      /**
  37       * Kses global for default allowable HTML tags.
  38       *
  39       * Can be override by using CUSTOM_TAGS constant.
  40       *
  41       * @global array $allowedposttags
  42       * @since 2.0.0
  43       */
  44      $allowedposttags = array(
  45          'address' => array(),
  46          'a' => array(
  47              'class' => array (),
  48              'href' => array (),
  49              'id' => array (),
  50              'title' => array (),
  51              'rel' => array (),
  52              'rev' => array (),
  53              'name' => array (),
  54              'target' => array()),
  55          'abbr' => array(
  56              'class' => array (),
  57              'title' => array ()),
  58          'acronym' => array(
  59              'title' => array ()),
  60          'b' => array(),
  61          'big' => array(),
  62          'blockquote' => array(
  63              'id' => array (),
  64              'cite' => array (),
  65              'class' => array(),
  66              'lang' => array(),
  67              'xml:lang' => array()),
  68          'br' => array (
  69              'class' => array ()),
  70          'button' => array(
  71              'disabled' => array (),
  72              'name' => array (),
  73              'type' => array (),
  74              'value' => array ()),
  75          'caption' => array(
  76              'align' => array (),
  77              'class' => array ()),
  78          'cite' => array (
  79              'class' => array(),
  80              'dir' => array(),
  81              'lang' => array(),
  82              'title' => array ()),
  83          'code' => array (
  84              'style' => array()),
  85          'col' => array(
  86              'align' => array (),
  87              'char' => array (),
  88              'charoff' => array (),
  89              'span' => array (),
  90              'dir' => array(),
  91              'style' => array (),
  92              'valign' => array (),
  93              'width' => array ()),
  94          'del' => array(
  95              'datetime' => array ()),
  96          'dd' => array(),
  97          'div' => array(
  98              'align' => array (),
  99              'class' => array (),
 100              'dir' => array (),
 101              'id' => array (),
 102              'lang' => array(),
 103              'style' => array (),
 104              'xml:lang' => array()),
 105          'dl' => array(),
 106          'dt' => array(),
 107          'em' => array(),
 108          'fieldset' => array(),
 109          'font' => array(
 110              'color' => array (),
 111              'face' => array (),
 112              'size' => array ()),
 113          'form' => array(
 114              'action' => array (),
 115              'accept' => array (),
 116              'accept-charset' => array (),
 117              'enctype' => array (),
 118              'method' => array (),
 119              'name' => array (),
 120              'target' => array ()),
 121          'h1' => array(
 122              'align' => array (),
 123              'class' => array (),
 124              'id'    => array (),
 125              'style' => array ()),
 126          'h2' => array (
 127              'align' => array (),
 128              'class' => array (),
 129              'id'    => array (),
 130              'style' => array ()),
 131          'h3' => array (
 132              'align' => array (),
 133              'class' => array (),
 134              'id'    => array (),
 135              'style' => array ()),
 136          'h4' => array (
 137              'align' => array (),
 138              'class' => array (),
 139              'id'    => array (),
 140              'style' => array ()),
 141          'h5' => array (
 142              'align' => array (),
 143              'class' => array (),
 144              'id'    => array (),
 145              'style' => array ()),
 146          'h6' => array (
 147              'align' => array (),
 148              'class' => array (),
 149              'id'    => array (),
 150              'style' => array ()),
 151          'hr' => array (
 152              'align' => array (),
 153              'class' => array (),
 154              'noshade' => array (),
 155              'size' => array (),
 156              'width' => array ()),
 157          'i' => array(),
 158          'img' => array(
 159              'alt' => array (),
 160              'align' => array (),
 161              'border' => array (),
 162              'class' => array (),
 163              'height' => array (),
 164              'hspace' => array (),
 165              'longdesc' => array (),
 166              'vspace' => array (),
 167              'src' => array (),
 168              'style' => array (),
 169              'title' => array (),
 170              'width' => array ()),
 171          'ins' => array(
 172              'datetime' => array (),
 173              'cite' => array ()),
 174          'kbd' => array(),
 175          'label' => array(
 176              'for' => array ()),
 177          'legend' => array(
 178              'align' => array ()),
 179          'li' => array (
 180              'align' => array (),
 181              'class' => array ()),
 182          'p' => array(
 183              'class' => array (),
 184              'align' => array (),
 185              'dir' => array(),
 186              'lang' => array(),
 187              'style' => array (),
 188              'xml:lang' => array()),
 189          'pre' => array(
 190              'style' => array(),
 191              'width' => array ()),
 192          'q' => array(
 193              'cite' => array ()),
 194          's' => array(),
 195          'span' => array (
 196              'class' => array (),
 197              'dir' => array (),
 198              'align' => array (),
 199              'lang' => array (),
 200              'style' => array (),
 201              'title' => array (),
 202              'xml:lang' => array()),
 203          'strike' => array(),
 204          'strong' => array(),
 205          'sub' => array(),
 206          'sup' => array(),
 207          'table' => array(
 208              'align' => array (),
 209              'bgcolor' => array (),
 210              'border' => array (),
 211              'cellpadding' => array (),
 212              'cellspacing' => array (),
 213              'class' => array (),
 214              'dir' => array(),
 215              'id' => array(),
 216              'rules' => array (),
 217              'style' => array (),
 218              'summary' => array (),
 219              'width' => array ()),
 220          'tbody' => array(
 221              'align' => array (),
 222              'char' => array (),
 223              'charoff' => array (),
 224              'valign' => array ()),
 225          'td' => array(
 226              'abbr' => array (),
 227              'align' => array (),
 228              'axis' => array (),
 229              'bgcolor' => array (),
 230              'char' => array (),
 231              'charoff' => array (),
 232              'class' => array (),
 233              'colspan' => array (),
 234              'dir' => array(),
 235              'headers' => array (),
 236              'height' => array (),
 237              'nowrap' => array (),
 238              'rowspan' => array (),
 239              'scope' => array (),
 240              'style' => array (),
 241              'valign' => array (),
 242              'width' => array ()),
 243          'textarea' => array(
 244              'cols' => array (),
 245              'rows' => array (),
 246              'disabled' => array (),
 247              'name' => array (),
 248              'readonly' => array ()),
 249          'tfoot' => array(
 250              'align' => array (),
 251              'char' => array (),
 252              'class' => array (),
 253              'charoff' => array (),
 254              'valign' => array ()),
 255          'th' => array(
 256              'abbr' => array (),
 257              'align' => array (),
 258              'axis' => array (),
 259              'bgcolor' => array (),
 260              'char' => array (),
 261              'charoff' => array (),
 262              'class' => array (),
 263              'colspan' => array (),
 264              'headers' => array (),
 265              'height' => array (),
 266              'nowrap' => array (),
 267              'rowspan' => array (),
 268              'scope' => array (),
 269              'valign' => array (),
 270              'width' => array ()),
 271          'thead' => array(
 272              'align' => array (),
 273              'char' => array (),
 274              'charoff' => array (),
 275              'class' => array (),
 276              'valign' => array ()),
 277          'title' => array(),
 278          'tr' => array(
 279              'align' => array (),
 280              'bgcolor' => array (),
 281              'char' => array (),
 282              'charoff' => array (),
 283              'class' => array (),
 284              'style' => array (),
 285              'valign' => array ()),
 286          'tt' => array(),
 287          'u' => array(),
 288          'ul' => array (
 289              'class' => array (),
 290              'style' => array (),
 291              'type' => array ()),
 292          'ol' => array (
 293              'class' => array (),
 294              'start' => array (),
 295              'style' => array (),
 296              'type' => array ()),
 297          'var' => array ());
 298  
 299      /**
 300       * Kses allowed HTML elements.
 301       *
 302       * @global array $allowedtags
 303       * @since 1.0.0
 304       */
 305      $allowedtags = array(
 306          'a' => array(
 307              'href' => array (),
 308              'title' => array ()),
 309          'abbr' => array(
 310              'title' => array ()),
 311          'acronym' => array(
 312              'title' => array ()),
 313          'b' => array(),
 314          'blockquote' => array(
 315              'cite' => array ()),
 316          //    'br' => array(),
 317          'cite' => array (),
 318          'code' => array(),
 319          'del' => array(
 320              'datetime' => array ()),
 321          //    'dd' => array(),
 322          //    'dl' => array(),
 323          //    'dt' => array(),
 324          'em' => array (), 'i' => array (),
 325          //    'ins' => array('datetime' => array(), 'cite' => array()),
 326          //    'li' => array(),
 327          //    'ol' => array(),
 328          //    'p' => array(),
 329          'q' => array(
 330              'cite' => array ()),
 331          'strike' => array(),
 332          'strong' => array(),
 333          //    'sub' => array(),
 334          //    'sup' => array(),
 335          //    'u' => array(),
 336          //    'ul' => array(),
 337      );
 338  }
 339  
 340  /**
 341   * Filters content and keeps only allowable HTML elements.
 342   *
 343   * This function makes sure that only the allowed HTML element names, attribute
 344   * names and attribute values plus only sane HTML entities will occur in
 345   * $string. You have to remove any slashes from PHP's magic quotes before you
 346   * call this function.
 347   *
 348   * The default allowed protocols are 'http', 'https', 'ftp', 'mailto', 'news',
 349   * 'irc', 'gopher', 'nntp', 'feed', and finally 'telnet. This covers all common
 350   * link protocols, except for 'javascript' which should not be allowed for
 351   * untrusted users.
 352   *
 353   * @since 1.0.0
 354   *
 355   * @param string $string Content to filter through kses
 356   * @param array $allowed_html List of allowed HTML elements
 357   * @param array $allowed_protocols Optional. Allowed protocol in links.
 358   * @return string Filtered content with only allowed HTML elements
 359   */
 360  function wp_kses($string, $allowed_html, $allowed_protocols = array ('http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet')) {
 361      $string = wp_kses_no_null($string);
 362      $string = wp_kses_js_entities($string);
 363      $string = wp_kses_normalize_entities($string);
 364      $allowed_html_fixed = wp_kses_array_lc($allowed_html);
 365      $string = wp_kses_hook($string, $allowed_html_fixed, $allowed_protocols); // WP changed the order of these funcs and added args to wp_kses_hook
 366      return wp_kses_split($string, $allowed_html_fixed, $allowed_protocols);
 367  }
 368  
 369  /**
 370   * You add any kses hooks here.
 371   *
 372   * There is currently only one kses WordPress hook and it is called here. All
 373   * parameters are passed to the hooks and expected to recieve a string.
 374   *
 375   * @since 1.0.0
 376   *
 377   * @param string $string Content to filter through kses
 378   * @param array $allowed_html List of allowed HTML elements
 379   * @param array $allowed_protocols Allowed protocol in links
 380   * @return string Filtered content through 'pre_kses' hook
 381   */
 382  function wp_kses_hook($string, $allowed_html, $allowed_protocols) {
 383      $string = apply_filters('pre_kses', $string, $allowed_html, $allowed_protocols);
 384      return $string;
 385  }
 386  
 387  /**
 388   * This function returns kses' version number.
 389   *
 390   * @since 1.0.0
 391   *
 392   * @return string KSES Version Number
 393   */
 394  function wp_kses_version() {
 395      return '0.2.2';
 396  }
 397  
 398  /**
 399   * Searches for HTML tags, no matter how malformed.
 400   *
 401   * It also matches stray ">" characters.
 402   *
 403   * @since 1.0.0
 404   *
 405   * @param string $string Content to filter
 406   * @param array $allowed_html Allowed HTML elements
 407   * @param array $allowed_protocols Allowed protocols to keep
 408   * @return string Content with fixed HTML tags
 409   */
 410  function wp_kses_split($string, $allowed_html, $allowed_protocols) {
 411      global $pass_allowed_html, $pass_allowed_protocols;
 412      $pass_allowed_html = $allowed_html;
 413      $pass_allowed_protocols = $allowed_protocols;
 414      return preg_replace_callback('%((<!--.*?(-->|$))|(<[^>]*(>|$)|>))%',
 415          create_function('$match', 'global $pass_allowed_html, $pass_allowed_protocols; return wp_kses_split2($match[1], $pass_allowed_html, $pass_allowed_protocols);'), $string);
 416  }
 417  
 418  /**
 419   * Callback for wp_kses_split for fixing malformed HTML tags.
 420   *
 421   * This function does a lot of work. It rejects some very malformed things like
 422   * <:::>. It returns an empty string, if the element isn't allowed (look ma, no
 423   * strip_tags()!). Otherwise it splits the tag into an element and an attribute
 424   * list.
 425   *
 426   * After the tag is split into an element and an attribute list, it is run
 427   * through another filter which will remove illegal attributes and once that is
 428   * completed, will be returned.
 429   *
 430   * @access private
 431   * @since 1.0.0
 432   * @uses wp_kses_attr()
 433   *
 434   * @param string $string Content to filter
 435   * @param array $allowed_html Allowed HTML elements
 436   * @param array $allowed_protocols Allowed protocols to keep
 437   * @return string Fixed HTML element
 438   */
 439  function wp_kses_split2($string, $allowed_html, $allowed_protocols) {
 440      $string = wp_kses_stripslashes($string);
 441  
 442      if (substr($string, 0, 1) != '<')
 443          return '&gt;';
 444      # It matched a ">" character
 445  
 446      if (preg_match('%^<!--(.*?)(-->)?$%', $string, $matches)) {
 447          $string = str_replace(array('<!--', '-->'), '', $matches[1]);
 448          while ( $string != $newstring = wp_kses($string, $allowed_html, $allowed_protocols) )
 449              $string = $newstring;
 450          if ( $string == '' )
 451              return '';
 452          // prevent multiple dashes in comments
 453          $string = preg_replace('/--+/', '-', $string);
 454          // prevent three dashes closing a comment
 455          $string = preg_replace('/-$/', '', $string);
 456          return "<!--{$string}-->";
 457      }
 458      # Allow HTML comments
 459  
 460      if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string, $matches))
 461          return '';
 462      # It's seriously malformed
 463  
 464      $slash = trim($matches[1]);
 465      $elem = $matches[2];
 466      $attrlist = $matches[3];
 467  
 468      if (!@isset($allowed_html[strtolower($elem)]))
 469          return '';
 470      # They are using a not allowed HTML element
 471  
 472      if ($slash != '')
 473          return "<$slash$elem>";
 474      # No attributes are allowed for closing elements
 475  
 476      return wp_kses_attr("$slash$elem", $attrlist, $allowed_html, $allowed_protocols);
 477  }
 478  
 479  /**
 480   * Removes all attributes, if none are allowed for this element.
 481   *
 482   * If some are allowed it calls wp_kses_hair() to split them further, and then
 483   * it builds up new HTML code from the data that kses_hair() returns. It also
 484   * removes "<" and ">" characters, if there are any left. One more thing it does
 485   * is to check if the tag has a closing XHTML slash, and if it does, it puts one
 486   * in the returned code as well.
 487   *
 488   * @since 1.0.0
 489   *
 490   * @param string $element HTML element/tag
 491   * @param string $attr HTML attributes from HTML element to closing HTML element tag
 492   * @param array $allowed_html Allowed HTML elements
 493   * @param array $allowed_protocols Allowed protocols to keep
 494   * @return string Sanitized HTML element
 495   */
 496  function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
 497      # Is there a closing XHTML slash at the end of the attributes?
 498  
 499      $xhtml_slash = '';
 500      if (preg_match('%\s/\s*$%', $attr))
 501          $xhtml_slash = ' /';
 502  
 503      # Are any attributes allowed at all for this element?
 504  
 505      if (@ count($allowed_html[strtolower($element)]) == 0)
 506          return "<$element$xhtml_slash>";
 507  
 508      # Split it
 509  
 510      $attrarr = wp_kses_hair($attr, $allowed_protocols);
 511  
 512      # Go through $attrarr, and save the allowed attributes for this element
 513      # in $attr2
 514  
 515      $attr2 = '';
 516  
 517      foreach ($attrarr as $arreach) {
 518          if (!@ isset ($allowed_html[strtolower($element)][strtolower($arreach['name'])]))
 519              continue; # the attribute is not allowed
 520  
 521          $current = $allowed_html[strtolower($element)][strtolower($arreach['name'])];
 522          if ($current == '')
 523              continue; # the attribute is not allowed
 524  
 525          if (!is_array($current))
 526              $attr2 .= ' '.$arreach['whole'];
 527          # there are no checks
 528  
 529          else {
 530              # there are some checks
 531              $ok = true;
 532              foreach ($current as $currkey => $currval)
 533                  if (!wp_kses_check_attr_val($arreach['value'], $arreach['vless'], $currkey, $currval)) {
 534                      $ok = false;
 535                      break;
 536                  }
 537  
 538              if ( $arreach['name'] == 'style' ) {
 539                  $orig_value = $arreach['value'];
 540  
 541                  $value = safecss_filter_attr($orig_value);
 542  
 543                  if ( empty($value) )
 544                      continue;
 545  
 546                  $arreach['value'] = $value;
 547  
 548                  $arreach['whole'] = str_replace($orig_value, $value, $arreach['whole']);
 549              }
 550  
 551              if ($ok)
 552                  $attr2 .= ' '.$arreach['whole']; # it passed them
 553          } # if !is_array($current)
 554      } # foreach
 555  
 556      # Remove any "<" or ">" characters
 557  
 558      $attr2 = preg_replace('/[<>]/', '', $attr2);
 559  
 560      return "<$element$attr2$xhtml_slash>";
 561  }
 562  
 563  /**
 564   * Builds an attribute list from string containing attributes.
 565   *
 566   * This function does a lot of work. It parses an attribute list into an array
 567   * with attribute data, and tries to do the right thing even if it gets weird
 568   * input. It will add quotes around attribute values that don't have any quotes
 569   * or apostrophes around them, to make it easier to produce HTML code that will
 570   * conform to W3C's HTML specification. It will also remove bad URL protocols
 571   * from attribute values.  It also reduces duplicate attributes by using the
 572   * attribute defined first (foo='bar' foo='baz' will result in foo='bar').
 573   *
 574   * @since 1.0.0
 575   *
 576   * @param string $attr Attribute list from HTML element to closing HTML element tag
 577   * @param array $allowed_protocols Allowed protocols to keep
 578   * @return array List of attributes after parsing
 579   */
 580  function wp_kses_hair($attr, $allowed_protocols) {
 581      $attrarr = array ();
 582      $mode = 0;
 583      $attrname = '';
 584      $uris = array('xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action');
 585  
 586      # Loop through the whole attribute list
 587  
 588      while (strlen($attr) != 0) {
 589          $working = 0; # Was the last operation successful?
 590  
 591          switch ($mode) {
 592              case 0 : # attribute name, href for instance
 593  
 594                  if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
 595                      $attrname = $match[1];
 596                      $working = $mode = 1;
 597                      $attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
 598                  }
 599  
 600                  break;
 601  
 602              case 1 : # equals sign or valueless ("selected")
 603  
 604                  if (preg_match('/^\s*=\s*/', $attr)) # equals sign
 605                      {
 606                      $working = 1;
 607                      $mode = 2;
 608                      $attr = preg_replace('/^\s*=\s*/', '', $attr);
 609                      break;
 610                  }
 611  
 612                  if (preg_match('/^\s+/', $attr)) # valueless
 613                      {
 614                      $working = 1;
 615                      $mode = 0;
 616                      if(FALSE === array_key_exists($attrname, $attrarr)) {
 617                          $attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
 618                      }
 619                      $attr = preg_replace('/^\s+/', '', $attr);
 620                  }
 621  
 622                  break;
 623  
 624              case 2 : # attribute value, a URL after href= for instance
 625  
 626                  if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match))
 627                      # "value"
 628                      {
 629                      $thisval = $match[1];
 630                      if ( in_array($attrname, $uris) )
 631                          $thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
 632  
 633                      if(FALSE === array_key_exists($attrname, $attrarr)) {
 634                          $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
 635                      }
 636                      $working = 1;
 637                      $mode = 0;
 638                      $attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
 639                      break;
 640                  }
 641  
 642                  if (preg_match("/^'([^']*)'(\s+|$)/", $attr, $match))
 643                      # 'value'
 644                      {
 645                      $thisval = $match[1];
 646                      if ( in_array($attrname, $uris) )
 647                          $thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
 648  
 649                      if(FALSE === array_key_exists($attrname, $attrarr)) {
 650                          $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname='$thisval'", 'vless' => 'n');
 651                      }
 652                      $working = 1;
 653                      $mode = 0;
 654                      $attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
 655                      break;
 656                  }
 657  
 658                  if (preg_match("%^([^\s\"']+)(\s+|$)%", $attr, $match))
 659                      # value
 660                      {
 661                      $thisval = $match[1];
 662                      if ( in_array($attrname, $uris) )
 663                          $thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
 664  
 665                      if(FALSE === array_key_exists($attrname, $attrarr)) {
 666                          $attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
 667                      }
 668                      # We add quotes to conform to W3C's HTML spec.
 669                      $working = 1;
 670                      $mode = 0;
 671                      $attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
 672                  }
 673  
 674                  break;
 675          } # switch
 676  
 677          if ($working == 0) # not well formed, remove and try again
 678          {
 679              $attr = wp_kses_html_error($attr);
 680              $mode = 0;
 681          }
 682      } # while
 683  
 684      if ($mode == 1 && FALSE === array_key_exists($attrname, $attrarr))
 685          # special case, for when the attribute list ends with a valueless
 686          # attribute like "selected"
 687          $attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
 688  
 689      return $attrarr;
 690  }
 691  
 692  /**
 693   * Performs different checks for attribute values.
 694   *
 695   * The currently implemented checks are "maxlen", "minlen", "maxval", "minval"
 696   * and "valueless" with even more checks to come soon.
 697   *
 698   * @since 1.0.0
 699   *
 700   * @param string $value Attribute value
 701   * @param string $vless Whether the value is valueless or not. Use 'y' or 'n'
 702   * @param string $checkname What $checkvalue is checking for.
 703   * @param mixed $checkvalue What constraint the value should pass
 704   * @return bool Whether check passes (true) or not (false)
 705   */
 706  function wp_kses_check_attr_val($value, $vless, $checkname, $checkvalue) {
 707      $ok = true;
 708  
 709      switch (strtolower($checkname)) {
 710          case 'maxlen' :
 711              # The maxlen check makes sure that the attribute value has a length not
 712              # greater than the given value. This can be used to avoid Buffer Overflows
 713              # in WWW clients and various Internet servers.
 714  
 715              if (strlen($value) > $checkvalue)
 716                  $ok = false;
 717              break;
 718  
 719          case 'minlen' :
 720              # The minlen check makes sure that the attribute value has a length not
 721              # smaller than the given value.
 722  
 723              if (strlen($value) < $checkvalue)
 724                  $ok = false;
 725              break;
 726  
 727          case 'maxval' :
 728              # The maxval check does two things: it checks that the attribute value is
 729              # an integer from 0 and up, without an excessive amount of zeroes or
 730              # whitespace (to avoid Buffer Overflows). It also checks that the attribute
 731              # value is not greater than the given value.
 732              # This check can be used to avoid Denial of Service attacks.
 733  
 734              if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
 735                  $ok = false;
 736              if ($value > $checkvalue)
 737                  $ok = false;
 738              break;
 739  
 740          case 'minval' :
 741              # The minval check checks that the attribute value is a positive integer,
 742              # and that it is not smaller than the given value.
 743  
 744              if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
 745                  $ok = false;
 746              if ($value < $checkvalue)
 747                  $ok = false;
 748              break;
 749  
 750          case 'valueless' :
 751              # The valueless check checks if the attribute has a value
 752              # (like <a href="blah">) or not (<option selected>). If the given value
 753              # is a "y" or a "Y", the attribute must not have a value.
 754              # If the given value is an "n" or an "N", the attribute must have one.
 755  
 756              if (strtolower($checkvalue) != $vless)
 757                  $ok = false;
 758              break;
 759      } # switch
 760  
 761      return $ok;
 762  }
 763  
 764  /**
 765   * Sanitize string from bad protocols.
 766   *
 767   * This function removes all non-allowed protocols from the beginning of
 768   * $string. It ignores whitespace and the case of the letters, and it does
 769   * understand HTML entities. It does its work in a while loop, so it won't be
 770   * fooled by a string like "javascript:javascript:alert(57)".
 771   *
 772   * @since 1.0.0
 773   *
 774   * @param string $string Content to filter bad protocols from
 775   * @param array $allowed_protocols Allowed protocols to keep
 776   * @return string Filtered content
 777   */
 778  function wp_kses_bad_protocol($string, $allowed_protocols) {
 779      $string = wp_kses_no_null($string);
 780      $string2 = $string.'a';
 781  
 782      while ($string != $string2) {
 783          $string2 = $string;
 784          $string = wp_kses_bad_protocol_once($string, $allowed_protocols);
 785      } # while
 786  
 787      return $string;
 788  }
 789  
 790  /**
 791   * Removes any NULL characters in $string.
 792   *
 793   * @since 1.0.0
 794   *
 795   * @param string $string
 796   * @return string
 797   */
 798  function wp_kses_no_null($string) {
 799      $string = preg_replace('/\0+/', '', $string);
 800      $string = preg_replace('/(\\\\0)+/', '', $string);
 801  
 802      return $string;
 803  }
 804  
 805  /**
 806   * Strips slashes from in front of quotes.
 807   *
 808   * This function changes the character sequence  \"  to just  ". It leaves all
 809   * other slashes alone. It's really weird, but the quoting from
 810   * preg_replace(//e) seems to require this.
 811   *
 812   * @since 1.0.0
 813   *
 814   * @param string $string String to strip slashes
 815   * @return string Fixed strings with quoted slashes
 816   */
 817  function wp_kses_stripslashes($string) {
 818      return preg_replace('%\\\\"%', '"', $string);
 819  }
 820  
 821  /**
 822   * Goes through an array and changes the keys to all lower case.
 823   *
 824   * @since 1.0.0
 825   *
 826   * @param array $inarray Unfiltered array
 827   * @return array Fixed array with all lowercase keys
 828   */
 829  function wp_kses_array_lc($inarray) {
 830      $outarray = array ();
 831  
 832      foreach ( (array) $inarray as $inkey => $inval) {
 833          $outkey = strtolower($inkey);
 834          $outarray[$outkey] = array ();
 835  
 836          foreach ( (array) $inval as $inkey2 => $inval2) {
 837              $outkey2 = strtolower($inkey2);
 838              $outarray[$outkey][$outkey2] = $inval2;
 839          } # foreach $inval
 840      } # foreach $inarray
 841  
 842      return $outarray;
 843  }
 844  
 845  /**
 846   * Removes the HTML JavaScript entities found in early versions of Netscape 4.
 847   *
 848   * @since 1.0.0
 849   *
 850   * @param string $string
 851   * @return string
 852   */
 853  function wp_kses_js_entities($string) {
 854      return preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
 855  }
 856  
 857  /**
 858   * Handles parsing errors in wp_kses_hair().
 859   *
 860   * The general plan is to remove everything to and including some whitespace,
 861   * but it deals with quotes and apostrophes as well.
 862   *
 863   * @since 1.0.0
 864   *
 865   * @param string $string
 866   * @return string
 867   */
 868  function wp_kses_html_error($string) {
 869      return preg_replace('/^("[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*/', '', $string);
 870  }
 871  
 872  /**
 873   * Sanitizes content from bad protocols and other characters.
 874   *
 875   * This function searches for URL protocols at the beginning of $string, while
 876   * handling whitespace and HTML entities.
 877   *
 878   * @since 1.0.0
 879   *
 880   * @param string $string Content to check for bad protocols
 881   * @param string $allowed_protocols Allowed protocols
 882   * @return string Sanitized content
 883   */
 884  function wp_kses_bad_protocol_once($string, $allowed_protocols) {
 885      global $_kses_allowed_protocols;
 886      $_kses_allowed_protocols = $allowed_protocols;
 887  
 888      $string2 = preg_split('/:|&#58;|&#x3a;/i', $string, 2);
 889      if ( isset($string2[1]) && !preg_match('%/\?%', $string2[0]) )
 890          $string = wp_kses_bad_protocol_once2($string2[0]) . trim($string2[1]);
 891      else
 892          $string = preg_replace_callback('/^((&[^;]*;|[\sA-Za-z0-9])*)'.'(:|&#58;|&#[Xx]3[Aa];)\s*/', 'wp_kses_bad_protocol_once2', $string);
 893  
 894      return $string;
 895  }
 896  
 897  /**
 898   * Callback for wp_kses_bad_protocol_once() regular expression.
 899   *
 900   * This function processes URL protocols, checks to see if they're in the
 901   * white-list or not, and returns different data depending on the answer.
 902   *
 903   * @access private
 904   * @since 1.0.0
 905   *
 906   * @param mixed $matches string or preg_replace_callback() matches array to check for bad protocols
 907   * @return string Sanitized content
 908   */
 909  function wp_kses_bad_protocol_once2($matches) {
 910      global $_kses_allowed_protocols;
 911  
 912      if ( is_array($matches) ) {
 913          if ( ! isset($matches[1]) || empty($matches[1]) )
 914              return '';
 915  
 916          $string = $matches[1];
 917      } else {
 918          $string = $matches;
 919      }
 920  
 921      $string2 = wp_kses_decode_entities($string);
 922      $string2 = preg_replace('/\s/', '', $string2);
 923      $string2 = wp_kses_no_null($string2);
 924      $string2 = strtolower($string2);
 925  
 926      $allowed = false;
 927      foreach ( (array) $_kses_allowed_protocols as $one_protocol)
 928          if (strtolower($one_protocol) == $string2) {
 929              $allowed = true;
 930              break;
 931          }
 932  
 933      if ($allowed)
 934          return "$string2:";
 935      else
 936          return '';
 937  }
 938  
 939  /**
 940   * Converts and fixes HTML entities.
 941   *
 942   * This function normalizes HTML entities. It will convert "AT&T" to the correct
 943   * "AT&amp;T", "&#00058;" to "&#58;", "&#XYZZY;" to "&amp;#XYZZY;" and so on.
 944   *
 945   * @since 1.0.0
 946   *
 947   * @param string $string Content to normalize entities
 948   * @return string Content with normalized entities
 949   */
 950  function wp_kses_normalize_entities($string) {
 951      # Disarm all entities by converting & to &amp;
 952  
 953      $string = str_replace('&', '&amp;', $string);
 954  
 955      # Change back the allowed entities in our entity whitelist
 956  
 957      $string = preg_replace('/&amp;([A-Za-z][A-Za-z0-9]{0,19});/', '&\\1;', $string);
 958      $string = preg_replace_callback('/&amp;#0*([0-9]{1,5});/', 'wp_kses_normalize_entities2', $string);
 959      $string = preg_replace_callback('/&amp;#([Xx])0*(([0-9A-Fa-f]{2}){1,2});/', 'wp_kses_normalize_entities3', $string);
 960  
 961      return $string;
 962  }
 963  
 964  /**
 965   * Callback for wp_kses_normalize_entities() regular expression.
 966   *
 967   * This function helps wp_kses_normalize_entities() to only accept 16 bit values
 968   * and nothing more for &#number; entities.
 969   *
 970   * @access private
 971   * @since 1.0.0
 972   *
 973   * @param array $matches preg_replace_callback() matches array
 974   * @return string Correctly encoded entity
 975   */
 976  function wp_kses_normalize_entities2($matches) {
 977      if ( ! isset($matches[1]) || empty($matches[1]) )
 978          return '';
 979  
 980      $i = $matches[1];
 981      return ( ( ! valid_unicode($i) ) || ($i > 65535) ? "&amp;#$i;" : "&#$i;" );
 982  }
 983  
 984  /**
 985   * Callback for wp_kses_normalize_entities() for regular expression.
 986   *
 987   * This function helps wp_kses_normalize_entities() to only accept valid Unicode
 988   * numeric entities in hex form.
 989   *
 990   * @access private
 991   *
 992   * @param array $matches preg_replace_callback() matches array
 993   * @return string Correctly encoded entity
 994   */
 995  function wp_kses_normalize_entities3($matches) {
 996      if ( ! isset($matches[2]) || empty($matches[2]) )
 997          return '';
 998  
 999      $hexchars = $matches[2];
1000      return ( ( ! valid_unicode(hexdec($hexchars)) ) ? "&amp;#x$hexchars;" : "&#x$hexchars;" );
1001  }
1002  
1003  /**
1004   * Helper function to determine if a Unicode value is valid.
1005   *
1006   * @param int $i Unicode value
1007   * @return bool true if the value was a valid Unicode number
1008   */
1009  function valid_unicode($i) {
1010      return ( $i == 0x9 || $i == 0xa || $i == 0xd ||
1011              ($i >= 0x20 && $i <= 0xd7ff) ||
1012              ($i >= 0xe000 && $i <= 0xfffd) ||
1013              ($i >= 0x10000 && $i <= 0x10ffff) );
1014  }
1015  
1016  /**
1017   * Convert all entities to their character counterparts.
1018   *
1019   * This function decodes numeric HTML entities (&#65; and &#x41;). It doesn't do
1020   * anything with other entities like &auml;, but we don't need them in the URL
1021   * protocol whitelisting system anyway.
1022   *
1023   * @since 1.0.0
1024   *
1025   * @param string $string Content to change entities
1026   * @return string Content after decoded entities
1027   */
1028  function wp_kses_decode_entities($string) {
1029      $string = preg_replace_callback('/&#([0-9]+);/', '_wp_kses_decode_entities_chr', $string);
1030      $string = preg_replace_callback('/&#[Xx]([0-9A-Fa-f]+);/', '_wp_kses_decode_entities_chr_hexdec', $string);
1031  
1032      return $string;
1033  }
1034  
1035  /**
1036   * Regex callback for wp_kses_decode_entities()
1037   *
1038   * @param array $match preg match
1039   * @return string
1040   */
1041  function _wp_kses_decode_entities_chr( $match ) {
1042      return chr( $match[1] );
1043  }
1044  
1045  /**
1046   * Regex callback for wp_kses_decode_entities()
1047   *
1048   * @param array $match preg match
1049   * @return string
1050   */
1051  function _wp_kses_decode_entities_chr_hexdec( $match ) {
1052      return chr( hexdec( $match[1] ) );
1053  }
1054  
1055  /**
1056   * Sanitize content with allowed HTML Kses rules.
1057   *
1058   * @since 1.0.0
1059   * @uses $allowedtags
1060   *
1061   * @param string $data Content to filter, expected to be escaped with slashes
1062   * @return string Filtered content
1063   */
1064  function wp_filter_kses($data) {
1065      global $allowedtags;
1066      return addslashes( wp_kses(stripslashes( $data ), $allowedtags) );
1067  }
1068  
1069  /**
1070   * Sanitize content with allowed HTML Kses rules.
1071   *
1072   * @since 2.9.0
1073   * @uses $allowedtags
1074   *
1075   * @param string $data Content to filter, expected to not be escaped
1076   * @return string Filtered content
1077   */
1078  function wp_kses_data($data) {
1079      global $allowedtags;
1080      return wp_kses( $data , $allowedtags );
1081  }
1082  
1083  /**
1084   * Sanitize content for allowed HTML tags for post content.
1085   *
1086   * Post content refers to the page contents of the 'post' type and not $_POST
1087   * data from forms.
1088   *
1089   * @since 2.0.0
1090   * @uses $allowedposttags
1091   *
1092   * @param string $data Post content to filter, expected to be escaped with slashes
1093   * @return string Filtered post content with allowed HTML tags and attributes intact.
1094   */
1095  function wp_filter_post_kses($data) {
1096      global $allowedposttags;
1097      return addslashes ( wp_kses(stripslashes( $data ), $allowedposttags) );
1098  }
1099  
1100  /**
1101   * Sanitize content for allowed HTML tags for post content.
1102   *
1103   * Post content refers to the page contents of the 'post' type and not $_POST
1104   * data from forms.
1105   *
1106   * @since 2.9.0
1107   * @uses $allowedposttags
1108   *
1109   * @param string $data Post content to filter
1110   * @return string Filtered post content with allowed HTML tags and attributes intact.
1111   */
1112  function wp_kses_post($data) {
1113      global $allowedposttags;
1114      return wp_kses( $data , $allowedposttags );
1115  }
1116  
1117  /**
1118   * Strips all of the HTML in the content.
1119   *
1120   * @since 2.1.0
1121   *
1122   * @param string $data Content to strip all HTML from
1123   * @return string Filtered content without any HTML
1124   */
1125  function wp_filter_nohtml_kses($data) {
1126      return addslashes ( wp_kses(stripslashes( $data ), array()) );
1127  }
1128  
1129  /**
1130   * Adds all Kses input form content filters.
1131   *
1132   * All hooks have default priority. The wp_filter_kses() function is added to
1133   * the 'pre_comment_content' and 'title_save_pre' hooks.
1134   *
1135   * The wp_filter_post_kses() function is added to the 'content_save_pre',
1136   * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks.
1137   *
1138   * @since 2.0.0
1139   * @uses add_filter() See description for what functions are added to what hooks.
1140   */
1141  function kses_init_filters() {
1142      // Normal filtering.
1143      add_filter('pre_comment_content', 'wp_filter_kses');
1144      add_filter('title_save_pre', 'wp_filter_kses');
1145  
1146      // Post filtering
1147      add_filter('content_save_pre', 'wp_filter_post_kses');
1148      add_filter('excerpt_save_pre', 'wp_filter_post_kses');
1149      add_filter('content_filtered_save_pre', 'wp_filter_post_kses');
1150  }
1151  
1152  /**
1153   * Removes all Kses input form content filters.
1154   *
1155   * A quick procedural method to removing all of the filters that kses uses for
1156   * content in WordPress Loop.
1157   *
1158   * Does not remove the kses_init() function from 'init' hook (priority is
1159   * default). Also does not remove kses_init() function from 'set_current_user'
1160   * hook (priority is also default).
1161   *
1162   * @since 2.0.6
1163   */
1164  function kses_remove_filters() {
1165      // Normal filtering.
1166      remove_filter('pre_comment_content', 'wp_filter_kses');
1167      remove_filter('title_save_pre', 'wp_filter_kses');
1168  
1169      // Post filtering
1170      remove_filter('content_save_pre', 'wp_filter_post_kses');
1171      remove_filter('excerpt_save_pre', 'wp_filter_post_kses');
1172      remove_filter('content_filtered_save_pre', 'wp_filter_post_kses');
1173  }
1174  
1175  /**
1176   * Sets up most of the Kses filters for input form content.
1177   *
1178   * If you remove the kses_init() function from 'init' hook and
1179   * 'set_current_user' (priority is default), then none of the Kses filter hooks
1180   * will be added.
1181   *
1182   * First removes all of the Kses filters in case the current user does not need
1183   * to have Kses filter the content. If the user does not have unfiltered html
1184   * capability, then Kses filters are added.
1185   *
1186   * @uses kses_remove_filters() Removes the Kses filters
1187   * @uses kses_init_filters() Adds the Kses filters back if the user
1188   *        does not have unfiltered HTML capability.
1189   * @since 2.0.0
1190   */
1191  function kses_init() {
1192      global $allowedposttags, $allowedtags;
1193      $allowedposttags = apply_filters( 'edit_allowedposttags', $allowedposttags );
1194      $allowedtags = apply_filters( 'edit_allowedtags', $allowedtags );
1195      kses_remove_filters();
1196  
1197      kses_init_filters();
1198  }
1199  
1200  add_action('init', 'kses_init');
1201  add_action('set_current_user', 'kses_init');
1202  
1203  function safecss_filter_attr( $css, $deprecated = '' ) {
1204      $css = wp_kses_no_null($css);
1205      $css = str_replace(array("\n","\r","\t"), '', $css);
1206  
1207      if ( preg_match( '%[\\(&]|/\*%', $css ) ) // remove any inline css containing \ ( & or comments
1208          return '';
1209  
1210      $css_array = split( ';', trim( $css ) );
1211      $allowed_attr = apply_filters( 'safe_style_css', array( 'text-align', 'margin', 'color', 'float',
1212      'border', 'background', 'background-color', 'border-bottom', 'border-bottom-color',
1213      'border-bottom-style', 'border-bottom-width', 'border-collapse', 'border-color', 'border-left',
1214      'border-left-color', 'border-left-style', 'border-left-width', 'border-right', 'border-right-color',
1215      'border-right-style', 'border-right-width', 'border-spacing', 'border-style', 'border-top',
1216      'border-top-color', 'border-top-style', 'border-top-width', 'border-width', 'caption-side',
1217      'clear', 'cursor', 'direction', 'font', 'font-family', 'font-size', 'font-style',
1218      'font-variant', 'font-weight', 'height', 'letter-spacing', 'line-height', 'margin-bottom',
1219      'margin-left', 'margin-right', 'margin-top', 'overflow', 'padding', 'padding-bottom',
1220      'padding-left', 'padding-right', 'padding-top', 'text-decoration', 'text-indent', 'vertical-align',
1221      'width' ) );
1222  
1223      if ( empty($allowed_attr) )
1224          return $css;
1225  
1226      $css = '';
1227      foreach ( $css_array as $css_item ) {
1228          if ( $css_item == '' )
1229              continue;
1230          $css_item = trim( $css_item );
1231          $found = false;
1232          if ( strpos( $css_item, ':' ) === false ) {
1233              $found = true;
1234          } else {
1235              $parts = split( ':', $css_item );
1236              if ( in_array( trim( $parts[0] ), $allowed_attr ) )
1237                  $found = true;
1238          }
1239          if ( $found ) {
1240              if( $css != '' )
1241                  $css .= ';';
1242              $css .= $css_item;
1243          }
1244      }
1245  
1246      return $css;
1247  }


Generated: Mon May 3 12:25:32 2010 Cross-referenced by PHPXref 0.7