wp/wp-includes/kses.php
changeset 5 5e2f62d02dcd
parent 0 d970ebf37754
child 7 cf61fcea0001
equal deleted inserted replaced
4:346c88efed21 5:5e2f62d02dcd
    41  *
    41  *
    42  * @since 1.2.0
    42  * @since 1.2.0
    43  */
    43  */
    44 if ( ! defined( 'CUSTOM_TAGS' ) )
    44 if ( ! defined( 'CUSTOM_TAGS' ) )
    45 	define( 'CUSTOM_TAGS', false );
    45 	define( 'CUSTOM_TAGS', false );
       
    46 
       
    47 // Ensure that these variables are added to the global namespace
       
    48 // (e.g. if using namespaces / autoload in the current PHP environment).
       
    49 global $allowedposttags, $allowedtags, $allowedentitynames;
    46 
    50 
    47 if ( ! CUSTOM_TAGS ) {
    51 if ( ! CUSTOM_TAGS ) {
    48 	/**
    52 	/**
    49 	 * Kses global for default allowable HTML tags.
    53 	 * Kses global for default allowable HTML tags.
    50 	 *
    54 	 *
    82 			'align' => true,
    86 			'align' => true,
    83 			'dir' => true,
    87 			'dir' => true,
    84 			'lang' => true,
    88 			'lang' => true,
    85 			'xml:lang' => true,
    89 			'xml:lang' => true,
    86 		),
    90 		),
       
    91 		'audio' => array(
       
    92 			'autoplay' => true,
       
    93 			'controls' => true,
       
    94 			'loop' => true,
       
    95 			'muted' => true,
       
    96 			'preload' => true,
       
    97 			'src' => true,
       
    98 		),
    87 		'b' => array(),
    99 		'b' => array(),
    88 		'big' => array(),
   100 		'big' => array(),
    89 		'blockquote' => array(
   101 		'blockquote' => array(
    90 			'cite' => true,
   102 			'cite' => true,
    91 			'lang' => true,
   103 			'lang' => true,
   113 			'span' => true,
   125 			'span' => true,
   114 			'dir' => true,
   126 			'dir' => true,
   115 			'valign' => true,
   127 			'valign' => true,
   116 			'width' => true,
   128 			'width' => true,
   117 		),
   129 		),
       
   130 		'colgroup' => array(
       
   131 			'align' => true,
       
   132 			'char' => true,
       
   133 			'charoff' => true,
       
   134 			'span' => true,
       
   135 			'valign' => true,
       
   136 			'width' => true,
       
   137 		),
   118 		'del' => array(
   138 		'del' => array(
   119 			'datetime' => true,
   139 			'datetime' => true,
   120 		),
   140 		),
   121 		'dd' => array(),
   141 		'dd' => array(),
       
   142 		'dfn' => array(),
   122 		'details' => array(
   143 		'details' => array(
   123 			'align' => true,
   144 			'align' => true,
   124 			'dir' => true,
   145 			'dir' => true,
   125 			'lang' => true,
   146 			'lang' => true,
   126 			'open' => true,
   147 			'open' => true,
   233 			'value' => true,
   254 			'value' => true,
   234 		),
   255 		),
   235 		'map' => array(
   256 		'map' => array(
   236 			'name' => true,
   257 			'name' => true,
   237 		),
   258 		),
       
   259 		'mark' => array(),
   238 		'menu' => array(
   260 		'menu' => array(
   239 			'type' => true,
   261 			'type' => true,
   240 		),
   262 		),
   241 		'nav' => array(
   263 		'nav' => array(
   242 			'align' => true,
   264 			'align' => true,
   255 		),
   277 		),
   256 		'q' => array(
   278 		'q' => array(
   257 			'cite' => true,
   279 			'cite' => true,
   258 		),
   280 		),
   259 		's' => array(),
   281 		's' => array(),
       
   282 		'samp' => array(),
   260 		'span' => array(
   283 		'span' => array(
   261 			'dir' => true,
   284 			'dir' => true,
   262 			'align' => true,
   285 			'align' => true,
   263 			'lang' => true,
   286 			'lang' => true,
   264 			'xml:lang' => true,
   287 			'xml:lang' => true,
   355 			'bgcolor' => true,
   378 			'bgcolor' => true,
   356 			'char' => true,
   379 			'char' => true,
   357 			'charoff' => true,
   380 			'charoff' => true,
   358 			'valign' => true,
   381 			'valign' => true,
   359 		),
   382 		),
       
   383 		'track' => array(
       
   384 			'default' => true,
       
   385 			'kind' => true,
       
   386 			'label' => true,
       
   387 			'src' => true,
       
   388 			'srclang' => true,
       
   389 		),
   360 		'tt' => array(),
   390 		'tt' => array(),
   361 		'u' => array(),
   391 		'u' => array(),
   362 		'ul' => array(
   392 		'ul' => array(
   363 			'type' => true,
   393 			'type' => true,
   364 		),
   394 		),
   365 		'ol' => array(
   395 		'ol' => array(
   366 			'start' => true,
   396 			'start' => true,
   367 			'type' => true,
   397 			'type' => true,
   368 		),
   398 		),
   369 		'var' => array(),
   399 		'var' => array(),
       
   400 		'video' => array(
       
   401 			'autoplay' => true,
       
   402 			'controls' => true,
       
   403 			'height' => true,
       
   404 			'loop' => true,
       
   405 			'muted' => true,
       
   406 			'poster' => true,
       
   407 			'preload' => true,
       
   408 			'src' => true,
       
   409 			'width' => true,
       
   410 		),
   370 	);
   411 	);
   371 
   412 
   372 	/**
   413 	/**
   373 	 * Kses allowed HTML elements.
   414 	 * Kses allowed HTML elements.
   374 	 *
   415 	 *
   398 		'em' => array(),
   439 		'em' => array(),
   399 		'i' => array(),
   440 		'i' => array(),
   400 		'q' => array(
   441 		'q' => array(
   401 			'cite' => true,
   442 			'cite' => true,
   402 		),
   443 		),
       
   444 		's' => array(),
   403 		'strike' => array(),
   445 		'strike' => array(),
   404 		'strong' => array(),
   446 		'strong' => array(),
   405 	);
   447 	);
   406 
   448 
   407 	$allowedentitynames = array(
   449 	$allowedentitynames = array(
   444 		'cap',     'cup',    'int',     'sim',    'cong',   'asymp',
   486 		'cap',     'cup',    'int',     'sim',    'cong',   'asymp',
   445 		'ne',      'equiv',  'le',      'ge',     'sub',    'sup',
   487 		'ne',      'equiv',  'le',      'ge',     'sub',    'sup',
   446 		'nsub',    'sube',   'supe',    'oplus',  'otimes', 'perp',
   488 		'nsub',    'sube',   'supe',    'oplus',  'otimes', 'perp',
   447 		'sdot',    'lceil',  'rceil',   'lfloor', 'rfloor', 'lang',
   489 		'sdot',    'lceil',  'rceil',   'lfloor', 'rfloor', 'lang',
   448 		'rang',    'loz',    'spades',  'clubs',  'hearts', 'diams',
   490 		'rang',    'loz',    'spades',  'clubs',  'hearts', 'diams',
       
   491 		'sup1',    'sup2',   'sup3',    'frac14', 'frac12', 'frac34',
       
   492 		'there4',
   449 	);
   493 	);
   450 
   494 
   451 	$allowedposttags = array_map( '_wp_add_global_attributes', $allowedposttags );
   495 	$allowedposttags = array_map( '_wp_add_global_attributes', $allowedposttags );
   452 } else {
   496 } else {
   453 	$allowedtags = wp_kses_array_lc( $allowedtags );
   497 	$allowedtags = wp_kses_array_lc( $allowedtags );
   494  * @return array List of allowed tags and their allowed attributes.
   538  * @return array List of allowed tags and their allowed attributes.
   495  */
   539  */
   496 function wp_kses_allowed_html( $context = '' ) {
   540 function wp_kses_allowed_html( $context = '' ) {
   497 	global $allowedposttags, $allowedtags, $allowedentitynames;
   541 	global $allowedposttags, $allowedtags, $allowedentitynames;
   498 
   542 
   499 	if ( is_array( $context ) )
   543 	if ( is_array( $context ) ) {
       
   544 		/**
       
   545 		 * Filter HTML elements allowed for a given context.
       
   546 		 *
       
   547 		 * @since 3.5.0
       
   548 		 *
       
   549 		 * @param string $tags    Allowed tags, attributes, and/or entities.
       
   550 		 * @param string $context Context to judge allowed tags by. Allowed values are 'post',
       
   551 		 *                        'data', 'strip', 'entities', 'explicit', or the name of a filter.
       
   552 		 */
   500 		return apply_filters( 'wp_kses_allowed_html', $context, 'explicit' );
   553 		return apply_filters( 'wp_kses_allowed_html', $context, 'explicit' );
       
   554 	}
   501 
   555 
   502 	switch ( $context ) {
   556 	switch ( $context ) {
   503 		case 'post':
   557 		case 'post':
       
   558 			/** This filter is documented in wp-includes/kses.php */
   504 			return apply_filters( 'wp_kses_allowed_html', $allowedposttags, $context );
   559 			return apply_filters( 'wp_kses_allowed_html', $allowedposttags, $context );
   505 			break;
   560 
   506 		case 'user_description':
   561 		case 'user_description':
   507 		case 'pre_user_description':
   562 		case 'pre_user_description':
   508 			$tags = $allowedtags;
   563 			$tags = $allowedtags;
   509 			$tags['a']['rel'] = true;
   564 			$tags['a']['rel'] = true;
       
   565 			/** This filter is documented in wp-includes/kses.php */
   510 			return apply_filters( 'wp_kses_allowed_html', $tags, $context );
   566 			return apply_filters( 'wp_kses_allowed_html', $tags, $context );
   511 			break;
   567 
   512 		case 'strip':
   568 		case 'strip':
       
   569 			/** This filter is documented in wp-includes/kses.php */
   513 			return apply_filters( 'wp_kses_allowed_html', array(), $context );
   570 			return apply_filters( 'wp_kses_allowed_html', array(), $context );
   514 			break;
   571 
   515 		case 'entities':
   572 		case 'entities':
       
   573 			/** This filter is documented in wp-includes/kses.php */
   516 			return apply_filters( 'wp_kses_allowed_html', $allowedentitynames, $context);
   574 			return apply_filters( 'wp_kses_allowed_html', $allowedentitynames, $context);
   517 			break;
   575 
   518 		case 'data':
   576 		case 'data':
   519 		default:
   577 		default:
       
   578 			/** This filter is documented in wp-includes/kses.php */
   520 			return apply_filters( 'wp_kses_allowed_html', $allowedtags, $context );
   579 			return apply_filters( 'wp_kses_allowed_html', $allowedtags, $context );
   521 	}
   580 	}
   522 }
   581 }
   523 
   582 
   524 /**
   583 /**
   533  * @param array $allowed_html List of allowed HTML elements
   592  * @param array $allowed_html List of allowed HTML elements
   534  * @param array $allowed_protocols Allowed protocol in links
   593  * @param array $allowed_protocols Allowed protocol in links
   535  * @return string Filtered content through 'pre_kses' hook
   594  * @return string Filtered content through 'pre_kses' hook
   536  */
   595  */
   537 function wp_kses_hook( $string, $allowed_html, $allowed_protocols ) {
   596 function wp_kses_hook( $string, $allowed_html, $allowed_protocols ) {
   538 	$string = apply_filters('pre_kses', $string, $allowed_html, $allowed_protocols);
   597 	/**
       
   598 	 * Filter content to be run through kses.
       
   599 	 *
       
   600 	 * @since 2.3.0
       
   601 	 *
       
   602 	 * @param string $string            Content to run through kses.
       
   603 	 * @param array  $allowed_html      Allowed HTML elements.
       
   604 	 * @param array  $allowed_protocols Allowed protocol in links.
       
   605 	 */
       
   606 	$string = apply_filters( 'pre_kses', $string, $allowed_html, $allowed_protocols );
   539 	return $string;
   607 	return $string;
   540 }
   608 }
   541 
   609 
   542 /**
   610 /**
   543  * This function returns kses' version number.
   611  * This function returns kses' version number.
   592  * through another filter which will remove illegal attributes and once that is
   660  * through another filter which will remove illegal attributes and once that is
   593  * completed, will be returned.
   661  * completed, will be returned.
   594  *
   662  *
   595  * @access private
   663  * @access private
   596  * @since 1.0.0
   664  * @since 1.0.0
   597  * @uses wp_kses_attr()
       
   598  *
   665  *
   599  * @param string $string Content to filter
   666  * @param string $string Content to filter
   600  * @param array $allowed_html Allowed HTML elements
   667  * @param array $allowed_html Allowed HTML elements
   601  * @param array $allowed_protocols Allowed protocols to keep
   668  * @param array $allowed_protocols Allowed protocols to keep
   602  * @return string Fixed HTML element
   669  * @return string Fixed HTML element
   604 function wp_kses_split2($string, $allowed_html, $allowed_protocols) {
   671 function wp_kses_split2($string, $allowed_html, $allowed_protocols) {
   605 	$string = wp_kses_stripslashes($string);
   672 	$string = wp_kses_stripslashes($string);
   606 
   673 
   607 	if (substr($string, 0, 1) != '<')
   674 	if (substr($string, 0, 1) != '<')
   608 		return '&gt;';
   675 		return '&gt;';
   609 	# It matched a ">" character
   676 	// It matched a ">" character
   610 
   677 
   611 	if ( '<!--' == substr( $string, 0, 4 ) ) {
   678 	if ( '<!--' == substr( $string, 0, 4 ) ) {
   612 		$string = str_replace( array('<!--', '-->'), '', $string );
   679 		$string = str_replace( array('<!--', '-->'), '', $string );
   613 		while ( $string != ($newstring = wp_kses($string, $allowed_html, $allowed_protocols)) )
   680 		while ( $string != ($newstring = wp_kses($string, $allowed_html, $allowed_protocols)) )
   614 			$string = $newstring;
   681 			$string = $newstring;
   618 		$string = preg_replace('/--+/', '-', $string);
   685 		$string = preg_replace('/--+/', '-', $string);
   619 		// prevent three dashes closing a comment
   686 		// prevent three dashes closing a comment
   620 		$string = preg_replace('/-$/', '', $string);
   687 		$string = preg_replace('/-$/', '', $string);
   621 		return "<!--{$string}-->";
   688 		return "<!--{$string}-->";
   622 	}
   689 	}
   623 	# Allow HTML comments
   690 	// Allow HTML comments
   624 
   691 
   625 	if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string, $matches))
   692 	if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $string, $matches))
   626 		return '';
   693 		return '';
   627 	# It's seriously malformed
   694 	// It's seriously malformed
   628 
   695 
   629 	$slash = trim($matches[1]);
   696 	$slash = trim($matches[1]);
   630 	$elem = $matches[2];
   697 	$elem = $matches[2];
   631 	$attrlist = $matches[3];
   698 	$attrlist = $matches[3];
   632 
   699 
   633 	if ( ! is_array( $allowed_html ) )
   700 	if ( ! is_array( $allowed_html ) )
   634 		$allowed_html = wp_kses_allowed_html( $allowed_html );
   701 		$allowed_html = wp_kses_allowed_html( $allowed_html );
   635 
   702 
   636 	if ( ! isset($allowed_html[strtolower($elem)]) )
   703 	if ( ! isset($allowed_html[strtolower($elem)]) )
   637 		return '';
   704 		return '';
   638 	# They are using a not allowed HTML element
   705 	// They are using a not allowed HTML element
   639 
   706 
   640 	if ($slash != '')
   707 	if ($slash != '')
   641 		return "</$elem>";
   708 		return "</$elem>";
   642 	# No attributes are allowed for closing elements
   709 	// No attributes are allowed for closing elements
   643 
   710 
   644 	return wp_kses_attr( $elem, $attrlist, $allowed_html, $allowed_protocols );
   711 	return wp_kses_attr( $elem, $attrlist, $allowed_html, $allowed_protocols );
   645 }
   712 }
   646 
   713 
   647 /**
   714 /**
   660  * @param array $allowed_html Allowed HTML elements
   727  * @param array $allowed_html Allowed HTML elements
   661  * @param array $allowed_protocols Allowed protocols to keep
   728  * @param array $allowed_protocols Allowed protocols to keep
   662  * @return string Sanitized HTML element
   729  * @return string Sanitized HTML element
   663  */
   730  */
   664 function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
   731 function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
   665 	# Is there a closing XHTML slash at the end of the attributes?
   732 	// Is there a closing XHTML slash at the end of the attributes?
   666 
   733 
   667 	if ( ! is_array( $allowed_html ) )
   734 	if ( ! is_array( $allowed_html ) )
   668 		$allowed_html = wp_kses_allowed_html( $allowed_html );
   735 		$allowed_html = wp_kses_allowed_html( $allowed_html );
   669 
   736 
   670 	$xhtml_slash = '';
   737 	$xhtml_slash = '';
   671 	if (preg_match('%\s*/\s*$%', $attr))
   738 	if (preg_match('%\s*/\s*$%', $attr))
   672 		$xhtml_slash = ' /';
   739 		$xhtml_slash = ' /';
   673 
   740 
   674 	# Are any attributes allowed at all for this element?
   741 	// Are any attributes allowed at all for this element?
   675 	if ( ! isset($allowed_html[strtolower($element)]) || count($allowed_html[strtolower($element)]) == 0 )
   742 	if ( ! isset($allowed_html[strtolower($element)]) || count($allowed_html[strtolower($element)]) == 0 )
   676 		return "<$element$xhtml_slash>";
   743 		return "<$element$xhtml_slash>";
   677 
   744 
   678 	# Split it
   745 	// Split it
   679 	$attrarr = wp_kses_hair($attr, $allowed_protocols);
   746 	$attrarr = wp_kses_hair($attr, $allowed_protocols);
   680 
   747 
   681 	# Go through $attrarr, and save the allowed attributes for this element
   748 	// Go through $attrarr, and save the allowed attributes for this element
   682 	# in $attr2
   749 	// in $attr2
   683 	$attr2 = '';
   750 	$attr2 = '';
   684 
   751 
   685 	$allowed_attr = $allowed_html[strtolower($element)];
   752 	$allowed_attr = $allowed_html[strtolower($element)];
   686 	foreach ($attrarr as $arreach) {
   753 	foreach ($attrarr as $arreach) {
   687 		if ( ! isset( $allowed_attr[strtolower($arreach['name'])] ) )
   754 		if ( ! isset( $allowed_attr[strtolower($arreach['name'])] ) )
   688 			continue; # the attribute is not allowed
   755 			continue; // the attribute is not allowed
   689 
   756 
   690 		$current = $allowed_attr[strtolower($arreach['name'])];
   757 		$current = $allowed_attr[strtolower($arreach['name'])];
   691 		if ( $current == '' )
   758 		if ( $current == '' )
   692 			continue; # the attribute is not allowed
   759 			continue; // the attribute is not allowed
   693 
   760 
   694 		if ( strtolower( $arreach['name'] ) == 'style' ) {
   761 		if ( strtolower( $arreach['name'] ) == 'style' ) {
   695 			$orig_value = $arreach['value'];
   762 			$orig_value = $arreach['value'];
   696 			$value = safecss_filter_attr( $orig_value );
   763 			$value = safecss_filter_attr( $orig_value );
   697 
   764 
   702 			$arreach['whole'] = str_replace( $orig_value, $value, $arreach['whole'] );
   769 			$arreach['whole'] = str_replace( $orig_value, $value, $arreach['whole'] );
   703 		}
   770 		}
   704 
   771 
   705 		if ( ! is_array($current) ) {
   772 		if ( ! is_array($current) ) {
   706 			$attr2 .= ' '.$arreach['whole'];
   773 			$attr2 .= ' '.$arreach['whole'];
   707 		# there are no checks
   774 		// there are no checks
   708 
   775 
   709 		} else {
   776 		} else {
   710 			# there are some checks
   777 			// there are some checks
   711 			$ok = true;
   778 			$ok = true;
   712 			foreach ($current as $currkey => $currval) {
   779 			foreach ($current as $currkey => $currval) {
   713 				if ( ! wp_kses_check_attr_val($arreach['value'], $arreach['vless'], $currkey, $currval) ) {
   780 				if ( ! wp_kses_check_attr_val($arreach['value'], $arreach['vless'], $currkey, $currval) ) {
   714 					$ok = false;
   781 					$ok = false;
   715 					break;
   782 					break;
   716 				}
   783 				}
   717 			}
   784 			}
   718 
   785 
   719 			if ( $ok )
   786 			if ( $ok )
   720 				$attr2 .= ' '.$arreach['whole']; # it passed them
   787 				$attr2 .= ' '.$arreach['whole']; // it passed them
   721 		} # if !is_array($current)
   788 		} // if !is_array($current)
   722 	} # foreach
   789 	} // foreach
   723 
   790 
   724 	# Remove any "<" or ">" characters
   791 	// Remove any "<" or ">" characters
   725 	$attr2 = preg_replace('/[<>]/', '', $attr2);
   792 	$attr2 = preg_replace('/[<>]/', '', $attr2);
   726 
   793 
   727 	return "<$element$attr2$xhtml_slash>";
   794 	return "<$element$attr2$xhtml_slash>";
   728 }
   795 }
   729 
   796 
   748 	$attrarr = array();
   815 	$attrarr = array();
   749 	$mode = 0;
   816 	$mode = 0;
   750 	$attrname = '';
   817 	$attrname = '';
   751 	$uris = array('xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action');
   818 	$uris = array('xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action');
   752 
   819 
   753 	# Loop through the whole attribute list
   820 	// Loop through the whole attribute list
   754 
   821 
   755 	while (strlen($attr) != 0) {
   822 	while (strlen($attr) != 0) {
   756 		$working = 0; # Was the last operation successful?
   823 		$working = 0; // Was the last operation successful?
   757 
   824 
   758 		switch ($mode) {
   825 		switch ($mode) {
   759 			case 0 : # attribute name, href for instance
   826 			case 0 : // attribute name, href for instance
   760 
   827 
   761 				if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
   828 				if ( preg_match('/^([-a-zA-Z:]+)/', $attr, $match ) ) {
   762 					$attrname = $match[1];
   829 					$attrname = $match[1];
   763 					$working = $mode = 1;
   830 					$working = $mode = 1;
   764 					$attr = preg_replace('/^[-a-zA-Z]+/', '', $attr);
   831 					$attr = preg_replace( '/^[-a-zA-Z:]+/', '', $attr );
   765 				}
   832 				}
   766 
   833 
   767 				break;
   834 				break;
   768 
   835 
   769 			case 1 : # equals sign or valueless ("selected")
   836 			case 1 : // equals sign or valueless ("selected")
   770 
   837 
   771 				if (preg_match('/^\s*=\s*/', $attr)) # equals sign
   838 				if (preg_match('/^\s*=\s*/', $attr)) // equals sign
   772 					{
   839 					{
   773 					$working = 1;
   840 					$working = 1;
   774 					$mode = 2;
   841 					$mode = 2;
   775 					$attr = preg_replace('/^\s*=\s*/', '', $attr);
   842 					$attr = preg_replace('/^\s*=\s*/', '', $attr);
   776 					break;
   843 					break;
   777 				}
   844 				}
   778 
   845 
   779 				if (preg_match('/^\s+/', $attr)) # valueless
   846 				if (preg_match('/^\s+/', $attr)) // valueless
   780 					{
   847 					{
   781 					$working = 1;
   848 					$working = 1;
   782 					$mode = 0;
   849 					$mode = 0;
   783 					if(false === array_key_exists($attrname, $attrarr)) {
   850 					if(false === array_key_exists($attrname, $attrarr)) {
   784 						$attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
   851 						$attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
   786 					$attr = preg_replace('/^\s+/', '', $attr);
   853 					$attr = preg_replace('/^\s+/', '', $attr);
   787 				}
   854 				}
   788 
   855 
   789 				break;
   856 				break;
   790 
   857 
   791 			case 2 : # attribute value, a URL after href= for instance
   858 			case 2 : // attribute value, a URL after href= for instance
   792 
   859 
   793 				if (preg_match('%^"([^"]*)"(\s+|/?$)%', $attr, $match))
   860 				if (preg_match('%^"([^"]*)"(\s+|/?$)%', $attr, $match))
   794 					# "value"
   861 					// "value"
   795 					{
   862 					{
   796 					$thisval = $match[1];
   863 					$thisval = $match[1];
   797 					if ( in_array(strtolower($attrname), $uris) )
   864 					if ( in_array(strtolower($attrname), $uris) )
   798 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
   865 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
   799 
   866 
   805 					$attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
   872 					$attr = preg_replace('/^"[^"]*"(\s+|$)/', '', $attr);
   806 					break;
   873 					break;
   807 				}
   874 				}
   808 
   875 
   809 				if (preg_match("%^'([^']*)'(\s+|/?$)%", $attr, $match))
   876 				if (preg_match("%^'([^']*)'(\s+|/?$)%", $attr, $match))
   810 					# 'value'
   877 					// 'value'
   811 					{
   878 					{
   812 					$thisval = $match[1];
   879 					$thisval = $match[1];
   813 					if ( in_array(strtolower($attrname), $uris) )
   880 					if ( in_array(strtolower($attrname), $uris) )
   814 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
   881 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
   815 
   882 
   821 					$attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
   888 					$attr = preg_replace("/^'[^']*'(\s+|$)/", '', $attr);
   822 					break;
   889 					break;
   823 				}
   890 				}
   824 
   891 
   825 				if (preg_match("%^([^\s\"']+)(\s+|/?$)%", $attr, $match))
   892 				if (preg_match("%^([^\s\"']+)(\s+|/?$)%", $attr, $match))
   826 					# value
   893 					// value
   827 					{
   894 					{
   828 					$thisval = $match[1];
   895 					$thisval = $match[1];
   829 					if ( in_array(strtolower($attrname), $uris) )
   896 					if ( in_array(strtolower($attrname), $uris) )
   830 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
   897 						$thisval = wp_kses_bad_protocol($thisval, $allowed_protocols);
   831 
   898 
   832 					if(false === array_key_exists($attrname, $attrarr)) {
   899 					if(false === array_key_exists($attrname, $attrarr)) {
   833 						$attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
   900 						$attrarr[$attrname] = array ('name' => $attrname, 'value' => $thisval, 'whole' => "$attrname=\"$thisval\"", 'vless' => 'n');
   834 					}
   901 					}
   835 					# We add quotes to conform to W3C's HTML spec.
   902 					// We add quotes to conform to W3C's HTML spec.
   836 					$working = 1;
   903 					$working = 1;
   837 					$mode = 0;
   904 					$mode = 0;
   838 					$attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
   905 					$attr = preg_replace("%^[^\s\"']+(\s+|$)%", '', $attr);
   839 				}
   906 				}
   840 
   907 
   841 				break;
   908 				break;
   842 		} # switch
   909 		} // switch
   843 
   910 
   844 		if ($working == 0) # not well formed, remove and try again
   911 		if ($working == 0) // not well formed, remove and try again
   845 		{
   912 		{
   846 			$attr = wp_kses_html_error($attr);
   913 			$attr = wp_kses_html_error($attr);
   847 			$mode = 0;
   914 			$mode = 0;
   848 		}
   915 		}
   849 	} # while
   916 	} // while
   850 
   917 
   851 	if ($mode == 1 && false === array_key_exists($attrname, $attrarr))
   918 	if ($mode == 1 && false === array_key_exists($attrname, $attrarr))
   852 		# special case, for when the attribute list ends with a valueless
   919 		// special case, for when the attribute list ends with a valueless
   853 		# attribute like "selected"
   920 		// attribute like "selected"
   854 		$attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
   921 		$attrarr[$attrname] = array ('name' => $attrname, 'value' => '', 'whole' => $attrname, 'vless' => 'y');
   855 
   922 
   856 	return $attrarr;
   923 	return $attrarr;
   857 }
   924 }
   858 
   925 
   873 function wp_kses_check_attr_val($value, $vless, $checkname, $checkvalue) {
   940 function wp_kses_check_attr_val($value, $vless, $checkname, $checkvalue) {
   874 	$ok = true;
   941 	$ok = true;
   875 
   942 
   876 	switch (strtolower($checkname)) {
   943 	switch (strtolower($checkname)) {
   877 		case 'maxlen' :
   944 		case 'maxlen' :
   878 			# The maxlen check makes sure that the attribute value has a length not
   945 			// The maxlen check makes sure that the attribute value has a length not
   879 			# greater than the given value. This can be used to avoid Buffer Overflows
   946 			// greater than the given value. This can be used to avoid Buffer Overflows
   880 			# in WWW clients and various Internet servers.
   947 			// in WWW clients and various Internet servers.
   881 
   948 
   882 			if (strlen($value) > $checkvalue)
   949 			if (strlen($value) > $checkvalue)
   883 				$ok = false;
   950 				$ok = false;
   884 			break;
   951 			break;
   885 
   952 
   886 		case 'minlen' :
   953 		case 'minlen' :
   887 			# The minlen check makes sure that the attribute value has a length not
   954 			// The minlen check makes sure that the attribute value has a length not
   888 			# smaller than the given value.
   955 			// smaller than the given value.
   889 
   956 
   890 			if (strlen($value) < $checkvalue)
   957 			if (strlen($value) < $checkvalue)
   891 				$ok = false;
   958 				$ok = false;
   892 			break;
   959 			break;
   893 
   960 
   894 		case 'maxval' :
   961 		case 'maxval' :
   895 			# The maxval check does two things: it checks that the attribute value is
   962 			// The maxval check does two things: it checks that the attribute value is
   896 			# an integer from 0 and up, without an excessive amount of zeroes or
   963 			// an integer from 0 and up, without an excessive amount of zeroes or
   897 			# whitespace (to avoid Buffer Overflows). It also checks that the attribute
   964 			// whitespace (to avoid Buffer Overflows). It also checks that the attribute
   898 			# value is not greater than the given value.
   965 			// value is not greater than the given value.
   899 			# This check can be used to avoid Denial of Service attacks.
   966 			// This check can be used to avoid Denial of Service attacks.
   900 
   967 
   901 			if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
   968 			if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
   902 				$ok = false;
   969 				$ok = false;
   903 			if ($value > $checkvalue)
   970 			if ($value > $checkvalue)
   904 				$ok = false;
   971 				$ok = false;
   905 			break;
   972 			break;
   906 
   973 
   907 		case 'minval' :
   974 		case 'minval' :
   908 			# The minval check makes sure that the attribute value is a positive integer,
   975 			// The minval check makes sure that the attribute value is a positive integer,
   909 			# and that it is not smaller than the given value.
   976 			// and that it is not smaller than the given value.
   910 
   977 
   911 			if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
   978 			if (!preg_match('/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value))
   912 				$ok = false;
   979 				$ok = false;
   913 			if ($value < $checkvalue)
   980 			if ($value < $checkvalue)
   914 				$ok = false;
   981 				$ok = false;
   915 			break;
   982 			break;
   916 
   983 
   917 		case 'valueless' :
   984 		case 'valueless' :
   918 			# The valueless check makes sure if the attribute has a value
   985 			// The valueless check makes sure if the attribute has a value
   919 			# (like <a href="blah">) or not (<option selected>). If the given value
   986 			// (like <a href="blah">) or not (<option selected>). If the given value
   920 			# is a "y" or a "Y", the attribute must not have a value.
   987 			// is a "y" or a "Y", the attribute must not have a value.
   921 			# If the given value is an "n" or an "N", the attribute must have one.
   988 			// If the given value is an "n" or an "N", the attribute must have one.
   922 
   989 
   923 			if (strtolower($checkvalue) != $vless)
   990 			if (strtolower($checkvalue) != $vless)
   924 				$ok = false;
   991 				$ok = false;
   925 			break;
   992 			break;
   926 	} # switch
   993 	} // switch
   927 
   994 
   928 	return $ok;
   995 	return $ok;
   929 }
   996 }
   930 
   997 
   931 /**
   998 /**
   956 
  1023 
   957 	return $string;
  1024 	return $string;
   958 }
  1025 }
   959 
  1026 
   960 /**
  1027 /**
   961  * Removes any null characters in $string.
  1028  * Removes any invalid control characters in $string.
       
  1029  *
       
  1030  * Also removes any instance of the '\0' string.
   962  *
  1031  *
   963  * @since 1.0.0
  1032  * @since 1.0.0
   964  *
  1033  *
   965  * @param string $string
  1034  * @param string $string
   966  * @return string
  1035  * @return string
   967  */
  1036  */
   968 function wp_kses_no_null($string) {
  1037 function wp_kses_no_null($string) {
   969 	$string = preg_replace('/\0+/', '', $string);
  1038 	$string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', $string);
   970 	$string = preg_replace('/(\\\\0)+/', '', $string);
  1039 	$string = preg_replace('/(\\\\0)+/', '', $string);
   971 
  1040 
   972 	return $string;
  1041 	return $string;
   973 }
  1042 }
   974 
  1043 
  1004 		$outarray[$outkey] = array ();
  1073 		$outarray[$outkey] = array ();
  1005 
  1074 
  1006 		foreach ( (array) $inval as $inkey2 => $inval2) {
  1075 		foreach ( (array) $inval as $inkey2 => $inval2) {
  1007 			$outkey2 = strtolower($inkey2);
  1076 			$outkey2 = strtolower($inkey2);
  1008 			$outarray[$outkey][$outkey2] = $inval2;
  1077 			$outarray[$outkey][$outkey2] = $inval2;
  1009 		} # foreach $inval
  1078 		} // foreach $inval
  1010 	} # foreach $inarray
  1079 	} // foreach $inarray
  1011 
  1080 
  1012 	return $outarray;
  1081 	return $outarray;
  1013 }
  1082 }
  1014 
  1083 
  1015 /**
  1084 /**
  1102 }
  1171 }
  1103 
  1172 
  1104 /**
  1173 /**
  1105  * Converts and fixes HTML entities.
  1174  * Converts and fixes HTML entities.
  1106  *
  1175  *
  1107  * This function normalizes HTML entities. It will convert "AT&T" to the correct
  1176  * This function normalizes HTML entities. It will convert `AT&T` to the correct
  1108  * "AT&amp;T", "&#00058;" to "&#58;", "&#XYZZY;" to "&amp;#XYZZY;" and so on.
  1177  * `AT&amp;T`, `&#00058;` to `&#58;`, `&#XYZZY;` to `&amp;#XYZZY;` and so on.
  1109  *
  1178  *
  1110  * @since 1.0.0
  1179  * @since 1.0.0
  1111  *
  1180  *
  1112  * @param string $string Content to normalize entities
  1181  * @param string $string Content to normalize entities
  1113  * @return string Content with normalized entities
  1182  * @return string Content with normalized entities
  1114  */
  1183  */
  1115 function wp_kses_normalize_entities($string) {
  1184 function wp_kses_normalize_entities($string) {
  1116 	# Disarm all entities by converting & to &amp;
  1185 	// Disarm all entities by converting & to &amp;
  1117 
  1186 
  1118 	$string = str_replace('&', '&amp;', $string);
  1187 	$string = str_replace('&', '&amp;', $string);
  1119 
  1188 
  1120 	# Change back the allowed entities in our entity whitelist
  1189 	// Change back the allowed entities in our entity whitelist
  1121 
  1190 
  1122 	$string = preg_replace_callback('/&amp;([A-Za-z]{2,8});/', 'wp_kses_named_entities', $string);
  1191 	$string = preg_replace_callback('/&amp;([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $string);
  1123 	$string = preg_replace_callback('/&amp;#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string);
  1192 	$string = preg_replace_callback('/&amp;#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string);
  1124 	$string = preg_replace_callback('/&amp;#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string);
  1193 	$string = preg_replace_callback('/&amp;#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string);
  1125 
  1194 
  1126 	return $string;
  1195 	return $string;
  1127 }
  1196 }
  1148 }
  1217 }
  1149 
  1218 
  1150 /**
  1219 /**
  1151  * Callback for wp_kses_normalize_entities() regular expression.
  1220  * Callback for wp_kses_normalize_entities() regular expression.
  1152  *
  1221  *
  1153  * This function helps wp_kses_normalize_entities() to only accept 16-bit values
  1222  * This function helps {@see wp_kses_normalize_entities()} to only accept 16-bit
  1154  * and nothing more for &#number; entities.
  1223  * values and nothing more for `&#number;` entities.
  1155  *
  1224  *
  1156  * @access private
  1225  * @access private
  1157  * @since 1.0.0
  1226  * @since 1.0.0
  1158  *
  1227  *
  1159  * @param array $matches preg_replace_callback() matches array
  1228  * @param array $matches preg_replace_callback() matches array
  1207 }
  1276 }
  1208 
  1277 
  1209 /**
  1278 /**
  1210  * Convert all entities to their character counterparts.
  1279  * Convert all entities to their character counterparts.
  1211  *
  1280  *
  1212  * This function decodes numeric HTML entities (&#65; and &#x41;). It doesn't do
  1281  * This function decodes numeric HTML entities (`&#65;` and `&#x41;`).
  1213  * anything with other entities like &auml;, but we don't need them in the URL
  1282  * It doesn't do anything with other entities like &auml;, but we don't
  1214  * protocol whitelisting system anyway.
  1283  * need them in the URL protocol whitelisting system anyway.
  1215  *
  1284  *
  1216  * @since 1.0.0
  1285  * @since 1.0.0
  1217  *
  1286  *
  1218  * @param string $string Content to change entities
  1287  * @param string $string Content to change entities
  1219  * @return string Content after decoded entities
  1288  * @return string Content after decoded entities
  1247 
  1316 
  1248 /**
  1317 /**
  1249  * Sanitize content with allowed HTML Kses rules.
  1318  * Sanitize content with allowed HTML Kses rules.
  1250  *
  1319  *
  1251  * @since 1.0.0
  1320  * @since 1.0.0
  1252  * @uses $allowedtags
       
  1253  *
  1321  *
  1254  * @param string $data Content to filter, expected to be escaped with slashes
  1322  * @param string $data Content to filter, expected to be escaped with slashes
  1255  * @return string Filtered content
  1323  * @return string Filtered content
  1256  */
  1324  */
  1257 function wp_filter_kses( $data ) {
  1325 function wp_filter_kses( $data ) {
  1260 
  1328 
  1261 /**
  1329 /**
  1262  * Sanitize content with allowed HTML Kses rules.
  1330  * Sanitize content with allowed HTML Kses rules.
  1263  *
  1331  *
  1264  * @since 2.9.0
  1332  * @since 2.9.0
  1265  * @uses $allowedtags
       
  1266  *
  1333  *
  1267  * @param string $data Content to filter, expected to not be escaped
  1334  * @param string $data Content to filter, expected to not be escaped
  1268  * @return string Filtered content
  1335  * @return string Filtered content
  1269  */
  1336  */
  1270 function wp_kses_data( $data ) {
  1337 function wp_kses_data( $data ) {
  1321  *
  1388  *
  1322  * The wp_filter_post_kses() function is added to the 'content_save_pre',
  1389  * The wp_filter_post_kses() function is added to the 'content_save_pre',
  1323  * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks.
  1390  * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks.
  1324  *
  1391  *
  1325  * @since 2.0.0
  1392  * @since 2.0.0
  1326  * @uses add_filter() See description for what functions are added to what hooks.
       
  1327  */
  1393  */
  1328 function kses_init_filters() {
  1394 function kses_init_filters() {
  1329 	// Normal filtering
  1395 	// Normal filtering
  1330 	add_filter('title_save_pre', 'wp_filter_kses');
  1396 	add_filter('title_save_pre', 'wp_filter_kses');
  1331 
  1397 
  1376  *
  1442  *
  1377  * First removes all of the Kses filters in case the current user does not need
  1443  * First removes all of the Kses filters in case the current user does not need
  1378  * to have Kses filter the content. If the user does not have unfiltered_html
  1444  * to have Kses filter the content. If the user does not have unfiltered_html
  1379  * capability, then Kses filters are added.
  1445  * capability, then Kses filters are added.
  1380  *
  1446  *
  1381  * @uses kses_remove_filters() Removes the Kses filters
       
  1382  * @uses kses_init_filters() Adds the Kses filters back if the user
       
  1383  *		does not have unfiltered HTML capability.
       
  1384  * @since 2.0.0
  1447  * @since 2.0.0
  1385  */
  1448  */
  1386 function kses_init() {
  1449 function kses_init() {
  1387 	kses_remove_filters();
  1450 	kses_remove_filters();
  1388 
  1451 
  1389 	if (current_user_can('unfiltered_html') == false)
  1452 	if (current_user_can('unfiltered_html') == false)
  1390 		kses_init_filters();
  1453 		kses_init_filters();
  1391 }
  1454 }
  1392 
       
  1393 add_action('init', 'kses_init');
       
  1394 add_action('set_current_user', 'kses_init');
       
  1395 
  1455 
  1396 /**
  1456 /**
  1397  * Inline CSS filter
  1457  * Inline CSS filter
  1398  *
  1458  *
  1399  * @since 2.8.1
  1459  * @since 2.8.1
  1403 		_deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented
  1463 		_deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented
  1404 
  1464 
  1405 	$css = wp_kses_no_null($css);
  1465 	$css = wp_kses_no_null($css);
  1406 	$css = str_replace(array("\n","\r","\t"), '', $css);
  1466 	$css = str_replace(array("\n","\r","\t"), '', $css);
  1407 
  1467 
  1408 	if ( preg_match( '%[\\(&=}]|/\*%', $css ) ) // remove any inline css containing \ ( & } = or comments
  1468 	if ( preg_match( '%[\\\\(&=}]|/\*%', $css ) ) // remove any inline css containing \ ( & } = or comments
  1409 		return '';
  1469 		return '';
  1410 
  1470 
  1411 	$css_array = explode( ';', trim( $css ) );
  1471 	$css_array = explode( ';', trim( $css ) );
       
  1472 
       
  1473 	/**
       
  1474 	 * Filter list of allowed CSS attributes.
       
  1475 	 *
       
  1476 	 * @since 2.8.1
       
  1477 	 *
       
  1478 	 * @param array $attr List of allowed CSS attributes.
       
  1479 	 */
  1412 	$allowed_attr = apply_filters( 'safe_style_css', array( 'text-align', 'margin', 'color', 'float',
  1480 	$allowed_attr = apply_filters( 'safe_style_css', array( 'text-align', 'margin', 'color', 'float',
  1413 	'border', 'background', 'background-color', 'border-bottom', 'border-bottom-color',
  1481 	'border', 'background', 'background-color', 'border-bottom', 'border-bottom-color',
  1414 	'border-bottom-style', 'border-bottom-width', 'border-collapse', 'border-color', 'border-left',
  1482 	'border-bottom-style', 'border-bottom-width', 'border-collapse', 'border-color', 'border-left',
  1415 	'border-left-color', 'border-left-style', 'border-left-width', 'border-right', 'border-right-color',
  1483 	'border-left-color', 'border-left-style', 'border-left-width', 'border-right', 'border-right-color',
  1416 	'border-right-style', 'border-right-width', 'border-spacing', 'border-style', 'border-top',
  1484 	'border-right-style', 'border-right-width', 'border-spacing', 'border-style', 'border-top',
  1460 	$global_attributes = array(
  1528 	$global_attributes = array(
  1461 		'class' => true,
  1529 		'class' => true,
  1462 		'id' => true,
  1530 		'id' => true,
  1463 		'style' => true,
  1531 		'style' => true,
  1464 		'title' => true,
  1532 		'title' => true,
       
  1533 		'role' => true,
  1465 	);
  1534 	);
  1466 
  1535 
  1467 	if ( true === $value )
  1536 	if ( true === $value )
  1468 		$value = array();
  1537 		$value = array();
  1469 
  1538