X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/11be8dc178e77d0b46189bbd8e33a216a9b90942..refs/tags/wordpress-2.8-scripts:/wp-includes/formatting.php diff --git a/wp-includes/formatting.php b/wp-includes/formatting.php index 53df8084..26bfc694 100644 --- a/wp-includes/formatting.php +++ b/wp-includes/formatting.php @@ -28,12 +28,20 @@ */ function wptexturize($text) { global $wp_cockneyreplace; - $next = true; - $has_pre_parent = false; $output = ''; $curl = ''; $textarr = preg_split('/(<.*>|\[.*\])/Us', $text, -1, PREG_SPLIT_DELIM_CAPTURE); $stop = count($textarr); + + /* translators: opening curly quote */ + $opening_quote = _x('“', 'opening curly quote'); + /* translators: closing curly quote */ + $closing_quote = _x('”', 'closing curly quote'); + + $no_texturize_tags = apply_filters('no_texturize_tags', array('pre', 'code', 'kbd', 'style', 'script', 'tt')); + $no_texturize_shortcodes = apply_filters('no_texturize_shortcodes', array('code')); + $no_texturize_tags_stack = array(); + $no_texturize_shortcodes_stack = array(); // if a plugin has provided an autocorrect array, use it if ( isset($wp_cockneyreplace) ) { @@ -44,28 +52,24 @@ function wptexturize($text) { $cockneyreplace = array("’tain’t","’twere","’twas","’tis","’twill","’til","’bout","’nuff","’round","’cause"); } - $static_characters = array_merge(array('---', ' -- ', '--', 'xn–', '...', '``', '\'s', '\'\'', ' (tm)'), $cockney); - $static_replacements = array_merge(array('—', ' — ', '–', 'xn--', '…', '“', '’s', '”', ' ™'), $cockneyreplace); + $static_characters = array_merge(array('---', ' -- ', '--', ' - ', 'xn–', '...', '``', '\'s', '\'\'', ' (tm)'), $cockney); + $static_replacements = array_merge(array('—', ' — ', '–', ' – ', 'xn--', '…', $opening_quote, '’s', $closing_quote, ' ™'), $cockneyreplace); $dynamic_characters = array('/\'(\d\d(?:’|\')?s)/', '/(\s|\A|")\'/', '/(\d+)"/', '/(\d+)\'/', '/(\S)\'([^\'\s])/', '/(\s|\A)"(?!\s)/', '/"(\s|\S|\Z)/', '/\'([\s.]|\Z)/', '/(\d+)x(\d+)/'); - $dynamic_replacements = array('’$1','$1‘', '$1″', '$1′', '$1’$2', '$1“$2', '”$1', '’$1', '$1×$2'); + $dynamic_replacements = array('’$1','$1‘', '$1″', '$1′', '$1’$2', '$1' . $opening_quote . '$2', $closing_quote . '$1', '’$1', '$1×$2'); for ( $i = 0; $i < $stop; $i++ ) { $curl = $textarr[$i]; - if ( !empty($curl) && '<' != $curl{0} && '[' != $curl{0} && $next && !$has_pre_parent) { // If it's not a tag + if ( !empty($curl) && '<' != $curl{0} && '[' != $curl{0} + && empty($no_texturize_shortcodes_stack) && empty($no_texturize_tags_stack)) { // If it's not a tag // static strings $curl = str_replace($static_characters, $static_replacements, $curl); // regular expressions $curl = preg_replace($dynamic_characters, $dynamic_replacements, $curl); - } elseif (strpos($curl, '') !== false) { - $has_pre_parent = false; } else { - $next = true; + wptexturize_pushpop_element($curl, $no_texturize_tags_stack, $no_texturize_tags, '<', '>'); + wptexturize_pushpop_element($curl, $no_texturize_shortcodes_stack, $no_texturize_shortcodes, '[', ']'); } $curl = preg_replace('/&([^#])(?![a-zA-Z1-4]{1,8};)/', '&$1', $curl); @@ -75,6 +79,21 @@ function wptexturize($text) { return $output; } +function wptexturize_pushpop_element($text, &$stack, $disabled_elements, $opening = '<', $closing = '>') { + $o = preg_quote($opening); + $c = preg_quote($closing); + foreach($disabled_elements as $element) { + if (preg_match('/^'.$o.$element.'\b/', $text)) array_push($stack, $element); + if (preg_match('/^'.$o.'\/'.$element.$c.'/', $text)) { + $last = array_pop($stack); + // disable texturize until we find a closing tag of our type (e.g.
)
+			// even if there was invalid nesting before that
+			// Example: in the case 
sadsadasd"baba"
"baba" won't be texturized + if ($last != $element) array_push($stack, $last); + } + } +} + /** * Accepts matches array from preg_replace_callback in wpautop() or a string. * @@ -114,10 +133,12 @@ function clean_pre($matches) { * @return string Text which has been converted into correct paragraph tags. */ function wpautop($pee, $br = 1) { + if ( trim($pee) === '' ) + return ''; $pee = $pee . "\n"; // just to make things a little easier, pad the end $pee = preg_replace('|
\s*
|', "\n\n", $pee); // Space things out a little - $allblocks = '(?:table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)'; + $allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)'; $pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee); $pee = preg_replace('!()!', "$1\n\n", $pee); $pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines @@ -131,9 +152,8 @@ function wpautop($pee, $br = 1) { $pee = ''; foreach ( $pees as $tinkle ) $pee .= '

' . trim($tinkle, "\n") . "

\n"; - $pee = preg_replace('|

\s*?

|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace - $pee = preg_replace('!

([^<]+)\s*?(]*>)!', "

$1

$2", $pee); - $pee = preg_replace( '|

|', "$1

", $pee ); + $pee = preg_replace('|

\s*

|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace + $pee = preg_replace('!

([^<]+)!', "

$1

", $pee); $pee = preg_replace('!

\s*(]*>)\s*

!', "$1", $pee); // don't pee all over a tag $pee = preg_replace("|

(|", "$1", $pee); // problem with nested lists $pee = preg_replace('|

]*)>|i', "

", $pee); @@ -148,7 +168,7 @@ function wpautop($pee, $br = 1) { $pee = preg_replace('!(]*>)\s*
!', "$1", $pee); $pee = preg_replace('!
(\s*]*>)!', '$1', $pee); if (strpos($pee, ')(.*?)

!is', 'clean_pre', $pee ); + $pee = preg_replace_callback('!(]*>)(.*?)!is', 'clean_pre', $pee ); $pee = preg_replace( "|\n

$|", '

', $pee ); $pee = preg_replace('/

\s*?(' . get_shortcode_regex() . ')\s*<\/p>/s', '$1', $pee); // don't auto-p wrap shortcodes that stand alone @@ -158,26 +178,29 @@ function wpautop($pee, $br = 1) { /** * Checks to see if a string is utf8 encoded. * - * @author bmorel at ssi dot fr + * NOTE: This function checks for 5-Byte sequences, UTF8 + * has Bytes Sequences with a maximum length of 4. * + * @author bmorel at ssi dot fr (modified) * @since 1.2.1 * - * @param string $Str The string to be checked - * @return bool True if $Str fits a UTF-8 model, false otherwise. + * @param string $str The string to be checked + * @return bool True if $str fits a UTF-8 model, false otherwise. */ -function seems_utf8($Str) { # by bmorel at ssi dot fr - $length = strlen($Str); +function seems_utf8($str) { + $length = strlen($str); for ($i=0; $i < $length; $i++) { - if (ord($Str[$i]) < 0x80) continue; # 0bbbbbbb - elseif ((ord($Str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb - elseif ((ord($Str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb - elseif ((ord($Str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb - elseif ((ord($Str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb - elseif ((ord($Str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b + $c = ord($str[$i]); + if ($c < 0x80) $n = 0; # 0bbbbbbb + elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb + elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb + elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb + elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb + elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b else return false; # Does not match any model for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ? - if ((++$i == $length) || ((ord($Str[$i]) & 0xC0) != 0x80)) - return false; + if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) + return false; } } return true; @@ -199,8 +222,7 @@ function seems_utf8($Str) { # by bmorel at ssi dot fr * @param boolean $double_encode Optional. Whether or not to encode existing html entities. Default is false. * @return string The encoded text with HTML entities. */ -function wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) -{ +function _wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) { $string = (string) $string; if ( 0 === strlen( $string ) ) { @@ -244,7 +266,7 @@ function wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false // Handle double encoding ourselves if ( !$double_encode ) { $string = wp_specialchars_decode( $string, $_quote_style ); - $string = preg_replace( '/&(#?x?[0-9]+|[a-z]+);/i', '|wp_entity|$1|/wp_entity|', $string ); + $string = preg_replace( '/&(#?x?[0-9a-z]+);/i', '|wp_entity|$1|/wp_entity|', $string ); } $string = @htmlspecialchars( $string, $quote_style, $charset ); @@ -273,11 +295,10 @@ function wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false * @since 2.8 * * @param string $string The text which is to be decoded. - * @param mixed $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old wp_specialchars() values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES. + * @param mixed $quote_style Optional. Converts double quotes if set to ENT_COMPAT, both single and double if set to ENT_QUOTES or none if set to ENT_NOQUOTES. Also compatible with old _wp_specialchars() values; converting single quotes if set to 'single', double if set to 'double' or both if otherwise set. Default is ENT_NOQUOTES. * @return string The decoded text without HTML entities. */ -function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) -{ +function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) { $string = (string) $string; if ( 0 === strlen( $string ) ) { @@ -289,7 +310,7 @@ function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) return $string; } - // Match the previous behaviour of wp_specialchars() when the $quote_style is not an accepted value + // Match the previous behaviour of _wp_specialchars() when the $quote_style is not an accepted value if ( empty( $quote_style ) ) { $quote_style = ENT_NOQUOTES; } elseif ( !in_array( $quote_style, array( 0, 2, 3, 'single', 'double' ), true ) ) { @@ -334,8 +355,7 @@ function wp_specialchars_decode( $string, $quote_style = ENT_NOQUOTES ) * @param boolean $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false. * @return string The checked text. */ -function wp_check_invalid_utf8( $string, $strip = false ) -{ +function wp_check_invalid_utf8( $string, $strip = false ) { $string = (string) $string; if ( 0 === strlen( $string ) ) { @@ -565,27 +585,27 @@ function remove_accents($string) { } /** - * Filters certain characters from the file name. + * Sanitizes a filename replacing whitespace with dashes * - * Turns all strings to lowercase removing most characters except alphanumeric - * with spaces, dashes and periods. All spaces and underscores are converted to - * dashes. Multiple dashes are converted to a single dash. Finally, if the file - * name ends with a dash, it is removed. + * Removes special characters that are illegal in filenames on certain + * operating systems and special characters requiring special escaping + * to manipulate at the command line. Replaces spaces and consecutive + * dashes with a single dash. Trim period, dash and underscore from beginning + * and end of filename. * * @since 2.1.0 * - * @param string $name The file name - * @return string Sanitized file name + * @param string $filename The filename to be sanitized + * @return string The sanitized filename */ -function sanitize_file_name( $name ) { // Like sanitize_title, but with periods - $name = strtolower( $name ); - $name = preg_replace('/&.+?;/', '', $name); // kill entities - $name = str_replace( '_', '-', $name ); - $name = preg_replace('/[^a-z0-9\s-.]/', '', $name); - $name = preg_replace('/\s+/', '-', $name); - $name = preg_replace('|-+|', '-', $name); - $name = trim($name, '-'); - return $name; +function sanitize_file_name( $filename ) { + $filename_raw = $filename; + $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}"); + $special_chars = apply_filters('sanitize_file_name_chars', $special_chars, $filename_raw); + $filename = str_replace($special_chars, '', $filename); + $filename = preg_replace('/[\s-]+/', '-', $filename); + $filename = trim($filename, '.-_'); + return apply_filters('sanitize_file_name', $filename, $filename_raw); } /** @@ -637,8 +657,9 @@ function sanitize_user( $username, $strict = false ) { * @return string The sanitized string. */ function sanitize_title($title, $fallback_title = '') { + $raw_title = $title; $title = strip_tags($title); - $title = apply_filters('sanitize_title', $title); + $title = apply_filters('sanitize_title', $title, $raw_title); if ( '' === $title || false === $title ) $title = $fallback_title; @@ -676,6 +697,7 @@ function sanitize_title_with_dashes($title) { $title = strtolower($title); $title = preg_replace('/&.+?;/', '', $title); // kill entities + $title = str_replace('.', '-', $title); $title = preg_replace('/[^%a-z0-9 _-]/', '', $title); $title = preg_replace('/\s+/', '-', $title); $title = preg_replace('|-+|', '-', $title); @@ -702,6 +724,33 @@ function sanitize_sql_orderby( $orderby ){ return $orderby; } +/** + * Santizes a html classname to ensure it only contains valid characters + * + * Strips the string down to A-Z,a-z,0-9,'-' if this results in an empty + * string then it will return the alternative value supplied. + * + * @todo Expand to support the full range of CDATA that a class attribute can contain. + * + * @since 2.8.0 + * + * @param string $class The classname to be sanitized + * @param string $fallback The value to return if the sanitization end's up as an empty string. + * @return string The sanitized value + */ +function sanitize_html_class($class, $fallback){ + //Strip out any % encoded octets + $sanitized = preg_replace('|%[a-fA-F0-9][a-fA-F0-9]|', '', $class); + + //Limit to A-Z,a-z,0-9,'-' + $sanitized = preg_replace('/[^A-Za-z0-9-]/', '', $sanitized); + + if ('' == $sanitized) + $sanitized = $fallback; + + return apply_filters('sanitize_html_class',$sanitized, $class, $fallback); +} + /** * Converts a number of characters from a string. * @@ -769,6 +818,18 @@ function convert_chars($content, $deprecated = '') { return $content; } +/** + * Callback used to change %uXXXX to &#YYY; syntax + * + * @since 2.8? + * + * @param array $matches Single Match + * @return string An HTML entity + */ +function funky_javascript_callback($matches) { + return "&#".base_convert($matches[1],16,10).";"; +} + /** * Fixes javascript bugs in browsers. * @@ -785,9 +846,10 @@ function funky_javascript_fix($text) { // Fixes for browsers' javascript bugs global $is_macIE, $is_winIE; - /** @todo use preg_replace_callback() instead */ if ( $is_winIE || $is_macIE ) - $text = preg_replace("/\%u([0-9A-F]{4,4})/e", "'&#'.base_convert('\\1',16,10).';'", $text); + $text = preg_replace_callback("/\%u([0-9A-F]{4,4})/", + "funky_javascript_callback", + $text); return $text; } @@ -848,7 +910,7 @@ function force_balance_tags( $text ) { // clear the shifter $tagqueue = ''; // Pop or Push - if ($regex[1][0] == "/") { // End Tag + if ( isset($regex[1][0]) && '/' == $regex[1][0] ) { // End Tag $tag = strtolower(substr($regex[1],1)); // if too many closing tags if($stacksize <= 0) { @@ -1122,17 +1184,11 @@ function antispambot($emailaddy, $mailto=0) { * @return string HTML A element with URI address. */ function _make_url_clickable_cb($matches) { - $ret = ''; $url = $matches[2]; - $url = clean_url($url); + $url = esc_url($url); if ( empty($url) ) return $matches[0]; - // removed trailing [.,;:] from URL - if ( in_array(substr($url, -1), array('.', ',', ';', ':')) === true ) { - $ret = substr($url, -1); - $url = substr($url, 0, strlen($url)-1); - } - return $matches[1] . "$url" . $ret; + return $matches[1] . "$url"; } /** @@ -1151,7 +1207,7 @@ function _make_web_ftp_clickable_cb($matches) { $ret = ''; $dest = $matches[2]; $dest = 'http://' . $dest; - $dest = clean_url($dest); + $dest = esc_url($dest); if ( empty($dest) ) return $matches[0]; // removed trailing [,;:] from URL @@ -1193,8 +1249,8 @@ function _make_email_clickable_cb($matches) { function make_clickable($ret) { $ret = ' ' . $ret; // in testing, using arrays here was found to be faster - $ret = preg_replace_callback('#([\s>])([\w]+?://[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]*)#is', '_make_url_clickable_cb', $ret); - $ret = preg_replace_callback('#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]*)#is', '_make_web_ftp_clickable_cb', $ret); + $ret = preg_replace_callback('#(?<=[\s>])(\()?([\w]+?://(?:[\w\\x80-\\xff\#$%&~/\-=?@\[\](+]|[.,;:](?![\s<])|(?(1)\)(?![\s<])|\)))+)#is', '_make_url_clickable_cb', $ret); + $ret = preg_replace_callback('#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret); $ret = preg_replace_callback('#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret); // this one is not in an array because we need it to run last, for cleanup of accidental links within links $ret = preg_replace("#(]+?>|>))]+?>([^>]+?)#i", "$1$3", $ret); @@ -1236,29 +1292,59 @@ function wp_rel_nofollow_callback( $matches ) { return ""; } + +/** + * Convert one smiley code to the icon graphic file equivalent. + * + * Looks up one smiley code in the $wpsmiliestrans global array and returns an + * string for that smiley. + * + * @global array $wpsmiliestrans + * @since 2.8.0 + * + * @param string $smiley Smiley code to convert to image. + * @return string Image string for smiley. + */ +function translate_smiley($smiley) { + global $wpsmiliestrans; + + if (count($smiley) == 0) { + return ''; + } + + $siteurl = get_option( 'siteurl' ); + + $smiley = trim(reset($smiley)); + $img = $wpsmiliestrans[$smiley]; + $smiley_masked = esc_attr($smiley); + + return " $smiley_masked "; +} + + /** * Convert text equivalent of smilies to images. * - * Will only convert smilies if the option 'use_smilies' is true and the globals - * used in the function aren't empty. + * Will only convert smilies if the option 'use_smilies' is true and the global + * used in the function isn't empty. * * @since 0.71 - * @uses $wp_smiliessearch, $wp_smiliesreplace Smiley replacement arrays. + * @uses $wp_smiliessearch * * @param string $text Content to convert smilies from text. * @return string Converted content with text smilies replaced with images. */ function convert_smilies($text) { - global $wp_smiliessearch, $wp_smiliesreplace; + global $wp_smiliessearch; $output = ''; - if ( get_option('use_smilies') && !empty($wp_smiliessearch) && !empty($wp_smiliesreplace) ) { + if ( get_option('use_smilies') && !empty($wp_smiliessearch) ) { // HTML loop taken from texturize function, could possible be consolidated $textarr = preg_split("/(<.*>)/U", $text, -1, PREG_SPLIT_DELIM_CAPTURE); // capture the tags as well as in between $stop = count($textarr);// loop stuff for ($i = 0; $i < $stop; $i++) { $content = $textarr[$i]; if ((strlen($content) > 0) && ('<' != $content{0})) { // If it's not a tag - $content = preg_replace($wp_smiliessearch, $wp_smiliesreplace, $content); + $content = preg_replace_callback($wp_smiliessearch, 'translate_smiley', $content); } $output .= $content; } @@ -1270,24 +1356,76 @@ function convert_smilies($text) { } /** - * Checks to see if the text is a valid email address. + * Verifies that an email is valid. + * + * Does not grok i18n domains. Not RFC compliant. * * @since 0.71 * - * @param string $user_email The email address to be checked. - * @return bool Returns true if valid, otherwise false. + * @param string $email Email address to verify. + * @param boolean $check_dns Whether to check the DNS for the domain using checkdnsrr(). + * @return string|bool Either false or the valid email address. */ -function is_email($user_email) { - $chars = "/^([a-z0-9+_]|\\-|\\.)+@(([a-z0-9_]|\\-)+\\.)+[a-z]{2,6}\$/i"; - if (strpos($user_email, '@') !== false && strpos($user_email, '.') !== false) { - if (preg_match($chars, $user_email)) { - return true; - } else { - return false; +function is_email( $email, $check_dns = false ) { + // Test for the minimum length the email can be + if ( strlen( $email ) < 3 ) { + return apply_filters( 'is_email', false, $email, 'email_too_short' ); + } + + // Test for an @ character after the first position + if ( strpos( $email, '@', 1 ) === false ) { + return apply_filters( 'is_email', false, $email, 'email_no_at' ); + } + + // Split out the local and domain parts + list( $local, $domain ) = explode( '@', $email, 2 ); + + // LOCAL PART + // Test for invalid characters + if ( !preg_match( '/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]+$/', $local ) ) { + return apply_filters( 'is_email', false, $email, 'local_invalid_chars' ); + } + + // DOMAIN PART + // Test for sequences of periods + if ( preg_match( '/\.{2,}/', $domain ) ) { + return apply_filters( 'is_email', false, $email, 'domain_period_sequence' ); + } + + // Test for leading and trailing periods and whitespace + if ( trim( $domain, " \t\n\r\0\x0B." ) !== $domain ) { + return apply_filters( 'is_email', false, $email, 'domain_period_limits' ); + } + + // Split the domain into subs + $subs = explode( '.', $domain ); + + // Assume the domain will have at least two subs + if ( 2 > count( $subs ) ) { + return apply_filters( 'is_email', false, $email, 'domain_no_periods' ); + } + + // Loop through each sub + foreach ( $subs as $sub ) { + // Test for leading and trailing hyphens and whitespace + if ( trim( $sub, " \t\n\r\0\x0B-" ) !== $sub ) { + return apply_filters( 'is_email', false, $email, 'sub_hyphen_limits' ); + } + + // Test for invalid characters + if ( !preg_match('/^[a-z0-9-]+$/i', $sub ) ) { + return apply_filters( 'is_email', false, $email, 'sub_invalid_chars' ); } - } else { - return false; } + + // DNS + // Check the domain has a valid MX and A resource record + if ( $check_dns && function_exists( 'checkdnsrr' ) && !( checkdnsrr( $domain . '.', 'MX' ) || checkdnsrr( $domain . '.', 'A' ) ) ) { + return apply_filters( 'is_email', false, $email, 'dns_no_rr' ); + } + + // Congratulations your email made it! + return apply_filters( 'is_email', $email, $email, null ); } /** @@ -1425,8 +1563,78 @@ function popuplinks($text) { * @param string $email Email address to filter. * @return string Filtered email address. */ -function sanitize_email($email) { - return preg_replace('/[^a-z0-9+_.@-]/i', '', $email); +function sanitize_email( $email ) { + // Test for the minimum length the email can be + if ( strlen( $email ) < 3 ) { + return apply_filters( 'sanitize_email', '', $email, 'email_too_short' ); + } + + // Test for an @ character after the first position + if ( strpos( $email, '@', 1 ) === false ) { + return apply_filters( 'sanitize_email', '', $email, 'email_no_at' ); + } + + // Split out the local and domain parts + list( $local, $domain ) = explode( '@', $email, 2 ); + + // LOCAL PART + // Test for invalid characters + $local = preg_replace( '/[^a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]/', '', $local ); + if ( '' === $local ) { + return apply_filters( 'sanitize_email', '', $email, 'local_invalid_chars' ); + } + + // DOMAIN PART + // Test for sequences of periods + $domain = preg_replace( '/\.{2,}/', '', $domain ); + if ( '' === $domain ) { + return apply_filters( 'sanitize_email', '', $email, 'domain_period_sequence' ); + } + + // Test for leading and trailing periods and whitespace + $domain = trim( $domain, " \t\n\r\0\x0B." ); + if ( '' === $domain ) { + return apply_filters( 'sanitize_email', '', $email, 'domain_period_limits' ); + } + + // Split the domain into subs + $subs = explode( '.', $domain ); + + // Assume the domain will have at least two subs + if ( 2 > count( $subs ) ) { + return apply_filters( 'sanitize_email', '', $email, 'domain_no_periods' ); + } + + // Create an array that will contain valid subs + $new_subs = array(); + + // Loop through each sub + foreach ( $subs as $sub ) { + // Test for leading and trailing hyphens + $sub = trim( $sub, " \t\n\r\0\x0B-" ); + + // Test for invalid characters + $sub = preg_replace( '/^[^a-z0-9-]+$/i', '', $sub ); + + // If there's anything left, add it to the valid subs + if ( '' !== $sub ) { + $new_subs[] = $sub; + } + } + + // If there aren't 2 or more valid subs + if ( 2 > count( $new_subs ) ) { + return apply_filters( 'sanitize_email', '', $email, 'domain_no_valid_subs' ); + } + + // Join valid subs into the new domain + $domain = join( '.', $new_subs ); + + // Put the email back together + $email = $local . '@' . $domain; + + // Congratulations your email made it! + return apply_filters( 'sanitize_email', $email, $email, null ); } /** @@ -1450,19 +1658,19 @@ function human_time_diff( $from, $to = '' ) { if ($mins <= 1) { $mins = 1; } - $since = sprintf(__ngettext('%s min', '%s mins', $mins), $mins); + $since = sprintf(_n('%s min', '%s mins', $mins), $mins); } else if (($diff <= 86400) && ($diff > 3600)) { $hours = round($diff / 3600); if ($hours <= 1) { $hours = 1; } - $since = sprintf(__ngettext('%s hour', '%s hours', $hours), $hours); + $since = sprintf(_n('%s hour', '%s hours', $hours), $hours); } elseif ($diff >= 86400) { $days = round($diff / 86400); if ($days <= 1) { $days = 1; } - $since = sprintf(__ngettext('%s day', '%s days', $days), $days); + $since = sprintf(_n('%s day', '%s days', $days), $days); } return $since; } @@ -1480,6 +1688,7 @@ function human_time_diff( $from, $to = '' ) { * @return string The excerpt. */ function wp_trim_excerpt($text) { + $raw_excerpt = $text; if ( '' == $text ) { $text = get_the_content(''); @@ -1496,7 +1705,7 @@ function wp_trim_excerpt($text) { $text = implode(' ', $words); } } - return $text; + return apply_filters('wp_trim_excerpt', $text, $raw_excerpt); } /** @@ -1815,7 +2024,7 @@ function wp_htmledit_pre($output) { * Checks and cleans a URL. * * A number of characters are removed from the URL. If the URL is for displaying - * (the default behaviour) amperstands are also replaced. The 'clean_url' filter + * (the default behaviour) amperstands are also replaced. The 'esc_url' filter * is applied to the returned cleaned URL. * * @since 1.2.0 @@ -1832,7 +2041,7 @@ function clean_url( $url, $protocols = null, $context = 'display' ) { $original_url = $url; if ('' == $url) return $url; - $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$*\'()\\x80-\\xff]|i', '', $url); + $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i', '', $url); $strip = array('%0d', '%0a'); $url = str_replace($strip, '', $url); $url = str_replace(';//', '://', $url); @@ -1841,7 +2050,7 @@ function clean_url( $url, $protocols = null, $context = 'display' ) { * link starting with / or a php file). */ if ( strpos($url, ':') === false && - substr( $url, 0, 1 ) != '/' && !preg_match('/^[a-z0-9-]+?\.php/i', $url) ) + substr( $url, 0, 1 ) != '/' && substr( $url, 0, 1 ) != '#' && !preg_match('/^[a-z0-9-]+?\.php/i', $url) ) $url = 'http://' . $url; // Replace ampersands and single quotes only when displaying. @@ -1859,9 +2068,62 @@ function clean_url( $url, $protocols = null, $context = 'display' ) { } /** - * Performs clean_url() for database usage. + * Escapes data for use in a MySQL query + * + * This is just a handy shortcut for $wpdb->escape(), for completeness' sake + * + * @since 2.8.0 + * @param string $sql Unescaped SQL data + * @return string The cleaned $sql + */ +function esc_sql( $sql ) { + global $wpdb; + return $wpdb->escape( $sql ); +} + + +/** + * Checks and cleans a URL. + * + * A number of characters are removed from the URL. If the URL is for displaying + * (the default behaviour) amperstands are also replaced. The 'esc_url' filter + * is applied to the returned cleaned URL. + * + * @since 2.8.0 + * @uses esc_url() + * @uses wp_kses_bad_protocol() To only permit protocols in the URL set + * via $protocols or the common ones set in the function. + * + * @param string $url The URL to be cleaned. + * @param array $protocols Optional. An array of acceptable protocols. + * Defaults to 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet' if not set. + * @return string The cleaned $url after the 'cleaned_url' filter is applied. + */ +function esc_url( $url, $protocols = null ) { + return clean_url( $url, $protocols, 'display' ); +} + +/** + * Performs esc_url() for database usage. + * + * @see esc_url() + * @see esc_url() * - * @see clean_url() + * @since 2.8.0 + * + * @param string $url The URL to be cleaned. + * @param array $protocols An array of acceptable protocols. + * @return string The cleaned URL. + */ +function esc_url_raw( $url, $protocols = null ) { + return clean_url( $url, $protocols, 'db' ); +} + +/** + * Performs esc_url() for database or redirect usage. + * + * @see esc_url() + * @deprecated 2.8.0 * * @since 2.3.1 * @@ -1894,33 +2156,94 @@ function htmlentities2($myHTML) { * * The filter 'js_escape' is also applied here. * - * @since 2.0.4 + * @since 2.8.0 * * @param string $text The text to be escaped. * @return string Escaped text. */ -function js_escape($text) { +function esc_js( $text ) { $safe_text = wp_check_invalid_utf8( $text ); - $safe_text = wp_specialchars( $safe_text, ENT_COMPAT ); + $safe_text = _wp_specialchars( $safe_text, ENT_COMPAT ); $safe_text = preg_replace( '/&#(x)?0*(?(1)27|39);?/i', "'", stripslashes( $safe_text ) ); $safe_text = preg_replace( "/\r?\n/", "\\n", addslashes( $safe_text ) ); return apply_filters( 'js_escape', $safe_text, $text ); } +/** + * Escape single quotes, specialchar double quotes, and fix line endings. + * + * The filter 'js_escape' is also applied by esc_js() + * + * @since 2.0.4 + * + * @deprecated 2.8.0 + * @see esc_js() + * + * @param string $text The text to be escaped. + * @return string Escaped text. + */ +function js_escape( $text ) { + return esc_js( $text ); +} + +/** + * Escaping for HTML blocks. + * + * @since 2.8.0 + * + * @param string $text + * @return string + */ +function esc_html( $text ) { + $safe_text = wp_check_invalid_utf8( $text ); + $safe_text = _wp_specialchars( $safe_text, ENT_QUOTES ); + return apply_filters( 'esc_html', $safe_text, $text ); + return $text; +} + +/** + * Escaping for HTML blocks + * @deprecated 2.8.0 + * @see esc_html() + */ +function wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) { + if ( func_num_args() > 1 ) { // Maintain backwards compat for people passing additional args + $args = func_get_args(); + return call_user_func_array( '_wp_specialchars', $args ); + } else { + return esc_html( $string ); + } +} + /** * Escaping for HTML attributes. * - * @since 2.0.6 + * @since 2.8.0 * * @param string $text * @return string */ -function attribute_escape( $text ) { +function esc_attr( $text ) { $safe_text = wp_check_invalid_utf8( $text ); - $safe_text = wp_specialchars( $safe_text, ENT_QUOTES ); + $safe_text = _wp_specialchars( $safe_text, ENT_QUOTES ); return apply_filters( 'attribute_escape', $safe_text, $text ); } +/** + * Escaping for HTML attributes. + * + * @since 2.0.6 + * + * @deprecated 2.8.0 + * @see esc_attr() + * + * @param string $text + * @return string + */ +function attribute_escape( $text ) { + return esc_attr( $text ); +} + /** * Escape a HTML tag name. * @@ -2019,7 +2342,7 @@ function sanitize_option($option, $value) { $value = addslashes($value); $value = wp_filter_post_kses( $value ); // calls stripslashes then addslashes $value = stripslashes($value); - $value = wp_specialchars( $value ); + $value = esc_html( $value ); break; case 'blog_charset': @@ -2046,7 +2369,7 @@ function sanitize_option($option, $value) { case 'siteurl': case 'home': $value = stripslashes($value); - $value = clean_url($value); + $value = esc_url($value); break; default : $value = apply_filters("sanitize_option_{$option}", $value, $option); @@ -2093,15 +2416,15 @@ function wp_pre_kses_less_than( $text ) { /** * Callback function used by preg_replace. * - * @uses wp_specialchars to format the $matches text. + * @uses esc_html to format the $matches text. * @since 2.3.0 * * @param array $matches Populated by matches to preg_replace. - * @return string The text returned after wp_specialchars if needed. + * @return string The text returned after esc_html if needed. */ function wp_pre_kses_less_than_callback( $matches ) { if ( false === strpos($matches[0], '>') ) - return wp_specialchars($matches[0]); + return esc_html($matches[0]); return $matches[0]; } @@ -2191,9 +2514,12 @@ function wp_sprintf_l($pattern, $args) { // Translate and filter the delimiter set (avoid ampersands and entities here) $l = apply_filters('wp_sprintf_l', array( - 'between' => _c(', |between list items'), - 'between_last_two' => _c(', and |between last two list items'), - 'between_only_two' => _c(' and |between only two list items'), + /* translators: used between list items, there is a space after the coma */ + 'between' => __(', '), + /* translators: used between list items, there is a space after the and */ + 'between_last_two' => __(', and '), + /* translators: used between only two list items, there is a space after the and */ + 'between_only_two' => __(' and '), )); $args = (array) $args; @@ -2205,7 +2531,7 @@ function wp_sprintf_l($pattern, $args) { while ( $i ) { $arg = array_shift($args); $i--; - if ( $i == 1 ) + if ( 0 == $i ) $result .= $l['between_last_two'] . $arg; else $result .= $l['between'] . $arg; @@ -2228,7 +2554,7 @@ function wp_sprintf_l($pattern, $args) { */ function wp_html_excerpt( $str, $count ) { $str = strip_tags( $str ); - $str = mb_strcut( $str, 0, $count ); + $str = mb_substr( $str, 0, $count ); // remove part of an entity at the end $str = preg_replace( '/&[^;\s]{0,6}$/', '', $str ); return $str;