Compare commits

..

15 Commits
4.0.3 ... 4.0.6

Author SHA1 Message Date
Dion Hulse
150dd442d2 Tag 4.0.6. Built from https://develop.svn.wordpress.org/@33403
git-svn-id: http://core.svn.wordpress.org/tags/4.0.6@33371 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-07-23 12:13:47 +00:00
Gary Pendergast
b3356b6f77 Bump 4.0 branch to version 4.0.6.
Built from https://develop.svn.wordpress.org/branches/4.0@33397


git-svn-id: http://core.svn.wordpress.org/branches/4.0@33365 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-07-23 11:07:33 +00:00
Gary Pendergast
60eb789f93 Shortcodes: Improve the reliablity of shortcodes inside HTML tags.
Merge of [33359] to the 4.0 branch.

Props miqrogroove.

See #15694.


Built from https://develop.svn.wordpress.org/branches/4.0@33381


git-svn-id: http://core.svn.wordpress.org/branches/4.0@33352 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-07-23 04:50:37 +00:00
Gary Pendergast
5d196f2b09 Capabilities: When creating an auto-draft, ensure that the current user still has permission to do so.
Merge of [33357] to the 4.0 branch.


Built from https://develop.svn.wordpress.org/branches/4.0@33376


git-svn-id: http://core.svn.wordpress.org/branches/4.0@33347 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-07-23 04:01:36 +00:00
Dominik Schilling
9e426bbb49 4.0.5 version bumps.
Built from https://develop.svn.wordpress.org/branches/4.0@32436


git-svn-id: http://core.svn.wordpress.org/branches/4.0@32406 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-05-06 23:28:36 +00:00
Michael Adams
cfd130d33a Upgrade: $wpdb->get_col_length() sanity check: bail on unexpected return value.
Merges [32429] for the 4.0 branch.

See #32165.

Built from https://develop.svn.wordpress.org/branches/4.0@32432


git-svn-id: http://core.svn.wordpress.org/branches/4.0@32402 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-05-06 23:13:58 +00:00
Michael Adams
551057bb68 Upgrade: Ensure unintelligible DB schemas don't result in content loss.
Merge of [32417] to the 4.0 branch.

See #32165.

Props ocean90.

Built from https://develop.svn.wordpress.org/branches/4.0@32420


git-svn-id: http://core.svn.wordpress.org/branches/4.0@32390 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-05-06 21:52:03 +00:00
John Blackbourn
6dbcbfae60 WPDB: Allow queries to reference tables in the dbname.tablename format, and allow table names to contain any valid character, rather than just ASCII.
Merge of [32368] to the 4.0 branch.

Props pento, willstedt for the initial patch.

See #32090.

Built from https://develop.svn.wordpress.org/branches/4.0@32413


git-svn-id: http://core.svn.wordpress.org/branches/4.0@32383 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-05-06 21:04:34 +00:00
Aaron Jorbin
a820c920f9 When upgrading WordPress remove genericons example.html files
[32385] for 4.0 branch

Props @dd32, @boonebgorges, @johnjamesjacoby, @drewapicture, @jorbin


Built from https://develop.svn.wordpress.org/branches/4.0@32404


git-svn-id: http://core.svn.wordpress.org/branches/4.0@32374 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-05-06 20:05:33 +00:00
Dominik Schilling
c9168e3a54 WPDB: When sanity checking query character sets, there's no need to check queries that don't return user data.
Merges [32374] to the 4.0 branch.

props pento.
see #32104.
Built from https://develop.svn.wordpress.org/branches/4.0@32403


git-svn-id: http://core.svn.wordpress.org/branches/4.0@32373 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-05-06 20:04:38 +00:00
Helen Hou-Sandí
69de48fd72 The UTF-8 regex can occasionally fail on very low memory machines. Reduce the amount of memory it uses.
Merges [32375] to the 4.0 branch.

props pento.
See #32204.

Built from https://develop.svn.wordpress.org/branches/4.0@32397


git-svn-id: http://core.svn.wordpress.org/branches/4.0@32367 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-05-06 19:43:37 +00:00
Michael Adams
7e35f92679 WPDB: When checking that a string can be sent to MySQL, we shouldn't use mb_convert_encoding(), as it behaves differently to MySQL's character encoding conversion.
Merge of [32364] to the 4.0 branch.

Props mdawaffe, pento, nbachiyski, jorbin, johnjamesjacoby, jeremyfelt.

See #32165.

Built from https://develop.svn.wordpress.org/branches/4.0@32388


git-svn-id: http://core.svn.wordpress.org/branches/4.0@32358 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-05-06 19:09:39 +00:00
Andrew Ozz
d6df175451 TinyMCE: work-around a bug in the tags matching regex.
For 4.0.
Built from https://develop.svn.wordpress.org/branches/4.0@32372


git-svn-id: http://core.svn.wordpress.org/branches/4.0@32342 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-05-06 05:34:38 +00:00
Gary Pendergast
4fe0310d7a 4.0:
- WPDB: Sanity check that any strings being stored in the DB are not too long to store correctly.
- When upgrading, remove any suspicious comments.


Built from https://develop.svn.wordpress.org/branches/4.0@32313


git-svn-id: http://core.svn.wordpress.org/branches/4.0@32284 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-04-27 17:17:37 +00:00
Gary Pendergast
4b6fa85282 4.0 branch is now 4.0.4.
Built from https://develop.svn.wordpress.org/branches/4.0@32302


git-svn-id: http://core.svn.wordpress.org/branches/4.0@32273 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2015-04-27 14:11:37 +00:00
17 changed files with 965 additions and 117 deletions

View File

@@ -9,7 +9,7 @@
<body>
<h1 id="logo">
<a href="https://wordpress.org/"><img alt="WordPress" src="wp-admin/images/wordpress-logo.png" /></a>
<br /> Version 4.0.3
<br /> Version 4.0.6
</h1>
<p style="text-align: center">Semantic Personal Publishing Platform</p>

View File

@@ -42,18 +42,30 @@ include( ABSPATH . 'wp-admin/admin-header.php' );
</h2>
<div class="changelog point-releases">
<h3><?php echo _n( 'Maintenance and Security Release', 'Maintenance and Security Releases', 3 ); ?></h3>
<h3><?php echo _n( 'Maintenance and Security Release', 'Maintenance and Security Releases', 6 ); ?></h3>
<p><?php printf( _n( '<strong>Version %1$s</strong> addressed a security issue.',
'<strong>Version %1$s</strong> addressed some security issues.', 2 ), '4.0.6' ); ?>
<?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), 'https://codex.wordpress.org/Version_4.0.6' ); ?>
</p>
<p><?php printf( _n( '<strong>Version %1$s</strong> addressed some security issues and fixed %2$s bug.',
'<strong>Version %1$s</strong> addressed some security issues and fixed %2$s bugs.', 3 ), '4.0.5', number_format_i18n( 3 ) ); ?>
<?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), 'https://codex.wordpress.org/Version_4.0.5' ); ?>
</p>
<p><?php printf( _n( '<strong>Version %1$s</strong> addressed a security issue.',
'<strong>Version %1$s</strong> addressed some security issues.', 1 ), '4.0.4' ); ?>
<?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), 'https://codex.wordpress.org/Version_4.0.4' ); ?>
</p>
<p><?php printf( _n( '<strong>Version %1$s</strong> addressed %2$s bug.',
'<strong>Version %1$s</strong> addressed %2$s bugs.', 1 ), '4.0.3', number_format_i18n( 1 ) ); ?>
<?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), 'https://codex.wordpress.org/Version_4.0.3' ); ?>
</p>
<p><?php printf( _n( '<strong>Version %1$s</strong> addressed a security issue.',
'<strong>Version %1$s</strong> addressed some security issues.', 8 ), '4.0.2' ); ?>
<?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), 'http://codex.wordpress.org/Version_4.0.2' ); ?>
<?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), 'https://codex.wordpress.org/Version_4.0.2' ); ?>
</p>
<p><?php printf( _n( '<strong>Version %1$s</strong> addressed some security issues and fixed %2$s bug.',
'<strong>Version %1$s</strong> addressed some security issues and fixed %2$s bugs.', 23 ), '4.0.1', number_format_i18n( 23 ) ); ?>
<?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), 'http://codex.wordpress.org/Version_4.0.1' ); ?>
<?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), 'https://codex.wordpress.org/Version_4.0.1' ); ?>
</p>
</div>

View File

@@ -416,6 +416,10 @@ function wp_network_dashboard_right_now() {
function wp_dashboard_quick_press( $error_msg = false ) {
global $post_ID;
if ( ! current_user_can( 'edit_posts' ) ) {
return;
}
/* Check if a new auto-draft (= no new post_ID) is needed or if the old can be used */
$last_post_id = (int) get_user_option( 'dashboard_quick_press_last_post_id' ); // Get the last post_ID
if ( $last_post_id ) {

View File

@@ -1011,6 +1011,9 @@ function update_core($from, $to) {
$wp_filesystem->delete($old_file, true);
}
// Remove any Genericons example.html's from the filesystem
_upgrade_422_remove_genericons();
// Upgrade DB with separate request
/** This filter is documented in wp-admin/includes/update-core.php */
apply_filters( 'update_feedback', __( 'Upgrading database&#8230;' ) );
@@ -1149,3 +1152,67 @@ window.location = 'about.php?updated';
exit();
}
add_action( '_core_updated_successfully', '_redirect_to_about_wordpress' );
/**
* Cleans up Genericons example files.
*
* @since 4.2.2
*/
function _upgrade_422_remove_genericons() {
global $wp_theme_directories, $wp_filesystem;
// A list of the affected files using the filesystem absolute paths.
$affected_files = array();
// Themes
foreach ( $wp_theme_directories as $directory ) {
$affected_theme_files = _upgrade_422_find_genericons_files_in_folder( $directory );
$affected_files = array_merge( $affected_files, $affected_theme_files );
}
// Plugins
$affected_plugin_files = _upgrade_422_find_genericons_files_in_folder( WP_PLUGIN_DIR );
$affected_files = array_merge( $affected_files, $affected_plugin_files );
foreach ( $affected_files as $file ) {
$gen_dir = $wp_filesystem->find_folder( trailingslashit( dirname( $file ) ) );
if ( empty( $gen_dir ) ) {
continue;
}
// The path when the file is accessed via WP_Filesystem may differ in the case of FTP
$remote_file = $gen_dir . basename( $file );
if ( ! $wp_filesystem->exists( $remote_file ) ) {
continue;
}
if ( ! $wp_filesystem->delete( $remote_file, false, 'f' ) ) {
$wp_filesystem->put_contents( $remote_file, '' );
}
}
}
/**
* Recursively find Genericons example files in a given folder.
*
* @ignore
* @since 4.2.2
*
* @param string $directory Directory path. Expects trailingslashed.
* @return array
*/
function _upgrade_422_find_genericons_files_in_folder( $directory ) {
$directory = trailingslashit( $directory );
$files = array();
if ( file_exists( "{$directory}example.html" ) && false !== strpos( file_get_contents( "{$directory}example.html" ), '<title>Genericons</title>' ) ) {
$files[] = "{$directory}example.html";
}
foreach ( glob( $directory . '*', GLOB_ONLYDIR ) as $dir ) {
$files = array_merge( $files, _upgrade_422_find_genericons_files_in_folder( $dir ) );
}
return $files;
}

View File

@@ -440,6 +440,9 @@ function upgrade_all() {
if ( $wp_current_db_version < 29630 )
upgrade_400();
if ( $wp_current_db_version < 29632 )
upgrade_405();
maybe_disable_link_manager();
maybe_disable_automattic_widgets();
@@ -1326,6 +1329,62 @@ function upgrade_400() {
}
}
/**
* Execute changes made in WordPress 4.0.4.
*
* @since 4.0.4
*/
function upgrade_404() {
}
/**
* Execute changes made in WordPress 4.0.5.
*
* @since 4.0.5
*/
function upgrade_405() {
global $wp_current_db_version, $wpdb;
if ( $wp_current_db_version < 29632 ) {
$content_length = $wpdb->get_col_length( $wpdb->comments, 'comment_content' );
if ( is_wp_error( $content_length ) ) {
return;
}
if ( false === $content_length ) {
$content_length = array(
'type' => 'byte',
'length' => 65535,
);
} elseif ( ! is_array( $content_length ) ) {
$length = (int) $content_length > 0 ? (int) $content_length : 65535;
$content_length = array(
'type' => 'byte',
'length' => $length
);
}
if ( 'byte' !== $content_length['type'] || 0 === $content_length['length'] ) {
// Sites with malformed DB schemas are on their own.
return;
}
$allowed_length = intval( $content_length['length'] ) - 10;
$comments = $wpdb->get_results(
"SELECT `comment_ID` FROM `{$wpdb->comments}`
WHERE `comment_date_gmt` > '2015-04-26'
AND LENGTH( `comment_content` ) >= {$allowed_length}
AND ( `comment_content` LIKE '%<%' OR `comment_content` LIKE '%>%' )"
);
foreach ( $comments as $comment ) {
wp_delete_comment( $comment->comment_ID, true );
}
}
}
/**
* Execute network level changes
*

View File

@@ -113,8 +113,9 @@ case 'post-quickdraft-save':
if ( ! wp_verify_nonce( $nonce, 'add-post' ) )
$error_msg = __( 'Unable to submit this form, please refresh and try again.' );
if ( ! current_user_can( 'edit_posts' ) )
$error_msg = __( 'Oops, you don&#8217;t have access to add new drafts.' );
if ( ! current_user_can( 'edit_posts' ) ) {
exit;
}
if ( $error_msg )
return wp_dashboard_quick_press( $error_msg );

View File

@@ -1114,8 +1114,10 @@ function map_meta_cap( $cap, $user_id ) {
case 'edit_post':
case 'edit_page':
$post = get_post( $args[0] );
if ( empty( $post ) )
if ( empty( $post ) ) {
$caps[] = 'do_not_allow';
break;
}
if ( 'revision' == $post->post_type ) {
$post = get_post( $post->post_parent );

View File

@@ -60,7 +60,7 @@ class WP_Embed {
add_shortcode( 'embed', array( $this, 'shortcode' ) );
// Do the shortcode (only the [embed] one is registered)
$content = do_shortcode( $content );
$content = do_shortcode( $content, true );
// Put the original shortcodes back
$shortcode_tags = $orig_shortcode_tags;
@@ -326,6 +326,10 @@ class WP_Embed {
* @return string Potentially modified $content.
*/
public function autoembed( $content ) {
// Strip newlines from all elements.
$content = wp_replace_in_html_tags( $content, array( "\n" => " " ) );
// Find URLs that are on their own line.
return preg_replace_callback( '|^\s*(https?://[^\s"]+)\s*$|im', array( $this, 'autoembed_callback' ), $content );
}

View File

@@ -13,23 +13,141 @@ if ( !function_exists('_') ) {
}
}
if ( !function_exists('mb_substr') ):
function mb_substr( $str, $start, $length=null, $encoding=null ) {
return _mb_substr($str, $start, $length, $encoding);
/**
* Returns whether PCRE/u (PCRE_UTF8 modifier) is available for use.
*
* @ignore
* @since 4.2.2
* @access private
*
* @param bool $set - Used for testing only
* null : default - get PCRE/u capability
* false : Used for testing - return false for future calls to this function
* 'reset': Used for testing - restore default behavior of this function
*/
function _wp_can_use_pcre_u( $set = null ) {
static $utf8_pcre = 'reset';
if ( null !== $set ) {
$utf8_pcre = $set;
}
if ( 'reset' === $utf8_pcre ) {
$utf8_pcre = @preg_match( '/^./u', 'a' );
}
return $utf8_pcre;
}
if ( ! function_exists( 'mb_substr' ) ) :
function mb_substr( $str, $start, $length = null, $encoding = null ) {
return _mb_substr( $str, $start, $length, $encoding );
}
endif;
function _mb_substr( $str, $start, $length=null, $encoding=null ) {
// the solution below, works only for utf-8, so in case of a different
// charset, just use built-in substr
$charset = get_option( 'blog_charset' );
if ( !in_array( $charset, array('utf8', 'utf-8', 'UTF8', 'UTF-8') ) ) {
return is_null( $length )? substr( $str, $start ) : substr( $str, $start, $length);
/*
* Only understands UTF-8 and 8bit. All other character sets will be treated as 8bit.
* For $encoding === UTF-8, the $str input is expected to be a valid UTF-8 byte sequence.
* The behavior of this function for invalid inputs is undefined.
*/
function _mb_substr( $str, $start, $length = null, $encoding = null ) {
if ( null === $encoding ) {
$encoding = get_option( 'blog_charset' );
}
// use the regex unicode support to separate the UTF-8 characters into an array
preg_match_all( '/./us', $str, $match );
$chars = is_null( $length )? array_slice( $match[0], $start ) : array_slice( $match[0], $start, $length );
return implode( '', $chars );
// The solution below works only for UTF-8,
// so in case of a different charset just use built-in substr()
if ( ! in_array( $encoding, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) {
return is_null( $length ) ? substr( $str, $start ) : substr( $str, $start, $length );
}
if ( _wp_can_use_pcre_u() ) {
// Use the regex unicode support to separate the UTF-8 characters into an array
preg_match_all( '/./us', $str, $match );
$chars = is_null( $length ) ? array_slice( $match[0], $start ) : array_slice( $match[0], $start, $length );
return implode( '', $chars );
}
$regex = '/(
[\x00-\x7F] # single-byte sequences 0xxxxxxx
| [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
| \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2
| [\xE1-\xEC][\x80-\xBF]{2}
| \xED[\x80-\x9F][\x80-\xBF]
| [\xEE-\xEF][\x80-\xBF]{2}
| \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3
| [\xF1-\xF3][\x80-\xBF]{3}
| \xF4[\x80-\x8F][\x80-\xBF]{2}
)/x';
$chars = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop
do {
// We had some string left over from the last round, but we counted it in that last round.
array_pop( $chars );
// Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
$pieces = preg_split( $regex, $str, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
$chars = array_merge( $chars, $pieces );
} while ( count( $pieces ) > 1 && $str = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
return join( '', array_slice( $chars, $start, $length ) );
}
if ( ! function_exists( 'mb_strlen' ) ) :
function mb_strlen( $str, $encoding = null ) {
return _mb_strlen( $str, $encoding );
}
endif;
/*
* Only understands UTF-8 and 8bit. All other character sets will be treated as 8bit.
* For $encoding === UTF-8, the $str input is expected to be a valid UTF-8 byte sequence.
* The behavior of this function for invalid inputs is undefined.
*/
function _mb_strlen( $str, $encoding = null ) {
if ( null === $encoding ) {
$encoding = get_option( 'blog_charset' );
}
// The solution below works only for UTF-8,
// so in case of a different charset just use built-in strlen()
if ( ! in_array( $encoding, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) {
return strlen( $str );
}
if ( _wp_can_use_pcre_u() ) {
// Use the regex unicode support to separate the UTF-8 characters into an array
preg_match_all( '/./us', $str, $match );
return count( $match[0] );
}
$regex = '/(?:
[\x00-\x7F] # single-byte sequences 0xxxxxxx
| [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
| \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2
| [\xE1-\xEC][\x80-\xBF]{2}
| \xED[\x80-\x9F][\x80-\xBF]
| [\xEE-\xEF][\x80-\xBF]{2}
| \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3
| [\xF1-\xF3][\x80-\xBF]{3}
| \xF4[\x80-\x8F][\x80-\xBF]{2}
)/x';
$count = 1; // Start at 1 instead of 0 since the first thing we do is decrement
do {
// We had some string left over from the last round, but we counted it in that last round.
$count--;
// Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
$pieces = preg_split( $regex, $str, 1000 );
// Increment
$count += count( $pieces );
} while ( $str = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
// Fencepost: preg_split() always returns one extra item in the array
return --$count;
}
if ( !function_exists('hash_hmac') ):

View File

@@ -405,6 +405,9 @@ function wpautop($pee, $br = true) {
$pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
$pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines
// Strip newlines from all elements.
$pee = wp_replace_in_html_tags( $pee, array( "\n" => " " ) );
if ( strpos( $pee, '<option' ) !== false ) {
// no P/BR around option
$pee = preg_replace( '|\s*<option|', '<option', $pee );
@@ -459,6 +462,74 @@ function wpautop($pee, $br = true) {
return $pee;
}
/**
* Replace characters or phrases within HTML elements only.
*
* @since 4.2.3
*
* @param string $haystack The text which has to be formatted.
* @param array $replace_pairs In the form array('from' => 'to', ...).
* @return string The formatted text.
*/
function wp_replace_in_html_tags( $haystack, $replace_pairs ) {
// Find all elements.
$comments =
'!' // Start of comment, after the <.
. '(?:' // Unroll the loop: Consume everything until --> is found.
. '-(?!->)' // Dash not followed by end of comment.
. '[^\-]*+' // Consume non-dashes.
. ')*+' // Loop possessively.
. '(?:-->)?'; // End of comment. If not found, match all input.
$regex =
'/(' // Capture the entire match.
. '<' // Find start of element.
. '(?(?=!--)' // Is this a comment?
. $comments // Find end of comment.
. '|'
. '[^>]*>?' // Find end of element. If not found, match all input.
. ')'
. ')/s';
$textarr = preg_split( $regex, $haystack, -1, PREG_SPLIT_DELIM_CAPTURE );
$changed = false;
// Optimize when searching for one item.
if ( 1 === count( $replace_pairs ) ) {
// Extract $needle and $replace.
foreach ( $replace_pairs as $needle => $replace );
// Loop through delimeters (elements) only.
for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
if ( false !== strpos( $textarr[$i], $needle ) ) {
$textarr[$i] = str_replace( $needle, $replace, $textarr[$i] );
$changed = true;
}
}
} else {
// Extract all $needles.
$needles = array_keys( $replace_pairs );
// Loop through delimeters (elements) only.
for ( $i = 1, $c = count( $textarr ); $i < $c; $i += 2 ) {
foreach ( $needles as $needle ) {
if ( false !== strpos( $textarr[$i], $needle ) ) {
$textarr[$i] = strtr( $textarr[$i], $replace_pairs );
$changed = true;
// After one strtr() break out of the foreach loop and look at next element.
break;
}
}
}
}
if ( $changed ) {
$haystack = implode( $textarr );
}
return $haystack;
}
/**
* Newline preservation help function for wpautop
*

View File

@@ -102,6 +102,10 @@ tinymce.PluginManager.add( 'wordpress', function( editor ) {
'<img src="' + tinymce.Env.transparentSrc + '" data-wp-more="nextpage" class="wp-more-tag mce-wp-nextpage" ' +
'title="' + title + '" data-mce-resize="false" data-mce-placeholder="1" />' );
}
if ( e.content.indexOf( '<?"' ) !== -1 ) {
e.content = e.content.replace( /<\?"/g, '' );
}
}
});

File diff suppressed because one or more lines are too long

View File

@@ -493,6 +493,82 @@ function wp_kses( $string, $allowed_html, $allowed_protocols = array() ) {
return wp_kses_split($string, $allowed_html, $allowed_protocols);
}
/**
* Filters one attribute only and ensures its value is allowed.
*
* This function has the advantage of being more secure than esc_attr() and can
* escape data in some situations where wp_kses() must strip the whole attribute.
*
* @since 4.2.3
*
* @param string $string The 'whole' attribute, including name and value.
* @param string $element The element name to which the attribute belongs.
* @return string Filtered attribute.
*/
function wp_kses_one_attr( $string, $element ) {
$uris = array('xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action');
$allowed_html = wp_kses_allowed_html( 'post' );
$allowed_protocols = wp_allowed_protocols();
$string = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) );
$string = wp_kses_js_entities( $string );
$string = wp_kses_normalize_entities( $string );
// Preserve leading and trailing whitespace.
$matches = array();
preg_match('/^\s*/', $string, $matches);
$lead = $matches[0];
preg_match('/\s*$/', $string, $matches);
$trail = $matches[0];
if ( empty( $trail ) ) {
$string = substr( $string, strlen( $lead ) );
} else {
$string = substr( $string, strlen( $lead ), -strlen( $trail ) );
}
// Parse attribute name and value from input.
$split = preg_split( '/\s*=\s*/', $string, 2 );
$name = $split[0];
if ( count( $split ) == 2 ) {
$value = $split[1];
// Remove quotes surrounding $value.
// Also guarantee correct quoting in $string for this one attribute.
if ( '' == $value ) {
$quote = '';
} else {
$quote = $value[0];
}
if ( '"' == $quote || "'" == $quote ) {
if ( substr( $value, -1 ) != $quote ) {
return '';
}
$value = substr( $value, 1, -1 );
} else {
$quote = '"';
}
// Sanitize quotes and angle braces.
$value = htmlspecialchars( $value, ENT_QUOTES, null, false );
// Sanitize URI values.
if ( in_array( strtolower( $name ), $uris ) ) {
$value = wp_kses_bad_protocol( $value, $allowed_protocols );
}
$string = "$name=$quote$value$quote";
$vless = 'n';
} else {
$value = '';
$vless = 'y';
}
// Sanitize attribute by name.
wp_kses_attr_check( $name, $value, $string, $vless, $element, $allowed_html );
// Restore whitespace.
return $lead . $string . $trail;
}
/**
* Return a list of allowed tags and attributes for a given context.
*
@@ -714,45 +790,11 @@ function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
# Go through $attrarr, and save the allowed attributes for this element
# in $attr2
$attr2 = '';
$allowed_attr = $allowed_html[strtolower($element)];
foreach ($attrarr as $arreach) {
if ( ! isset( $allowed_attr[strtolower($arreach['name'])] ) )
continue; # the attribute is not allowed
$current = $allowed_attr[strtolower($arreach['name'])];
if ( $current == '' )
continue; # the attribute is not allowed
if ( strtolower( $arreach['name'] ) == 'style' ) {
$orig_value = $arreach['value'];
$value = safecss_filter_attr( $orig_value );
if ( empty( $value ) )
continue;
$arreach['value'] = $value;
$arreach['whole'] = str_replace( $orig_value, $value, $arreach['whole'] );
}
if ( ! is_array($current) ) {
foreach ( $attrarr as $arreach ) {
if ( wp_kses_attr_check( $arreach['name'], $arreach['value'], $arreach['whole'], $arreach['vless'], $element, $allowed_html ) ) {
$attr2 .= ' '.$arreach['whole'];
# there are no checks
} else {
# there are some checks
$ok = true;
foreach ($current as $currkey => $currval) {
if ( ! wp_kses_check_attr_val($arreach['value'], $arreach['vless'], $currkey, $currval) ) {
$ok = false;
break;
}
}
if ( $ok )
$attr2 .= ' '.$arreach['whole']; # it passed them
} # if !is_array($current)
} # foreach
}
}
# Remove any "<" or ">" characters
$attr2 = preg_replace('/[<>]/', '', $attr2);
@@ -760,6 +802,53 @@ function wp_kses_attr($element, $attr, $allowed_html, $allowed_protocols) {
return "<$element$attr2$xhtml_slash>";
}
/**
* Determine whether an attribute is allowed.
*
* @since 4.2.3
*
* @param string $name The attribute name. Returns empty string when not allowed.
* @param string $value The attribute value. Returns a filtered value.
* @param string $whole The name=value input. Returns filtered input.
* @param string $vless 'y' when attribute like "enabled", otherwise 'n'.
* @param string $element The name of the element to which this attribute belongs.
* @param array $allowed_html The full list of allowed elements and attributes.
* @return bool Is the attribute allowed?
*/
function wp_kses_attr_check( &$name, &$value, &$whole, $vless, $element, $allowed_html ) {
$allowed_attr = $allowed_html[strtolower( $element )];
$name_low = strtolower( $name );
if ( ! isset( $allowed_attr[$name_low] ) || '' == $allowed_attr[$name_low] ) {
$name = $value = $whole = '';
return false;
}
if ( 'style' == $name_low ) {
$new_value = safecss_filter_attr( $value );
if ( empty( $new_value ) ) {
$name = $value = $whole = '';
return false;
}
$whole = str_replace( $value, $new_value, $whole );
$value = $new_value;
}
if ( is_array( $allowed_attr[$name_low] ) ) {
// there are some checks
foreach ( $allowed_attr[$name_low] as $currkey => $currval ) {
if ( ! wp_kses_check_attr_val( $value, $vless, $currkey, $currval ) ) {
$name = $value = $whole = '';
return false;
}
}
}
return true;
}
/**
* Builds an attribute list from string containing attributes.
*
@@ -889,6 +978,109 @@ function wp_kses_hair($attr, $allowed_protocols) {
return $attrarr;
}
/**
* Finds all attributes of an HTML element.
*
* Does not modify input. May return "evil" output.
*
* Based on wp_kses_split2() and wp_kses_attr()
*
* @since 4.2.3
*
* @param string $element HTML element/tag
* @return array|bool List of attributes found in $element. Returns false on failure.
*/
function wp_kses_attr_parse( $element ) {
$valid = preg_match('%^(<\s*)(/\s*)?([a-zA-Z0-9]+\s*)([^>]*)(>?)$%', $element, $matches);
if ( 1 !== $valid ) {
return false;
}
$begin = $matches[1];
$slash = $matches[2];
$elname = $matches[3];
$attr = $matches[4];
$end = $matches[5];
if ( '' !== $slash ) {
// Closing elements do not get parsed.
return false;
}
// Is there a closing XHTML slash at the end of the attributes?
if ( 1 === preg_match( '%\s*/\s*$%', $attr, $matches ) ) {
$xhtml_slash = $matches[0];
$attr = substr( $attr, 0, -strlen( $xhtml_slash ) );
} else {
$xhtml_slash = '';
}
// Split it
$attrarr = wp_kses_hair_parse( $attr );
if ( false === $attrarr ) {
return false;
}
// Make sure all input is returned by adding front and back matter.
array_unshift( $attrarr, $begin . $slash . $elname );
array_push( $attrarr, $xhtml_slash . $end );
return $attrarr;
}
/**
* Builds an attribute list from string containing attributes.
*
* Does not modify input. May return "evil" output.
* In case of unexpected input, returns false instead of stripping things.
*
* Based on wp_kses_hair() but does not return a multi-dimensional array.
*
* @since 4.2.3
*
* @param string $attr Attribute list from HTML element to closing HTML element tag
* @return array|bool List of attributes found in $attr. Returns false on failure.
*/
function wp_kses_hair_parse( $attr ) {
if ( '' === $attr ) {
return array();
}
$regex =
'(?:'
. '[-a-zA-Z:]+' // Attribute name.
. '|'
. '\[\[?[^\[\]]+\]\]?' // Shortcode in the name position implies unfiltered_html.
. ')'
. '(?:' // Attribute value.
. '\s*=\s*' // All values begin with '='
. '(?:'
. '"[^"]*"' // Double-quoted
. '|'
. "'[^']*'" // Single-quoted
. '|'
. '[^\s"\']+' // Non-quoted
. '(?:\s|$)' // Must have a space
. ')'
. '|'
. '(?:\s|$)' // If attribute has no value, space is required.
. ')'
. '\s*'; // Trailing space is optional except as mentioned above.
// Although it is possible to reduce this procedure to a single regexp,
// we must run that regexp twice to get exactly the expected result.
$validation = "%^($regex)+$%";
$extraction = "%$regex%";
if ( 1 === preg_match( $validation, $attr ) ) {
preg_match_all( $extraction, $attr, $attrarr );
return $attrarr[0];
} else {
return false;
}
}
/**
* Performs different checks for attribute values.
*

View File

@@ -188,9 +188,10 @@ function has_shortcode( $content, $tag ) {
* @uses get_shortcode_regex() Gets the search pattern for searching shortcodes.
*
* @param string $content Content to search for shortcodes
* @param bool $ignore_html When true, shortcodes inside HTML elements will be skipped.
* @return string Content with shortcodes filtered out.
*/
function do_shortcode($content) {
function do_shortcode( $content, $ignore_html = false ) {
global $shortcode_tags;
if ( false === strpos( $content, '[' ) ) {
@@ -200,8 +201,24 @@ function do_shortcode($content) {
if (empty($shortcode_tags) || !is_array($shortcode_tags))
return $content;
$tagnames = array_keys($shortcode_tags);
$tagregexp = join( '|', array_map('preg_quote', $tagnames) );
$pattern = "/\\[($tagregexp)/s";
if ( 1 !== preg_match( $pattern, $content ) ) {
// Avoids parsing HTML when there are no shortcodes or embeds anyway.
return $content;
}
$content = do_shortcodes_in_html_tags( $content, $ignore_html );
$pattern = get_shortcode_regex();
return preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $content );
$content = preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $content );
// Always restore square braces so we don't break things like <!--[if IE ]>
$content = unescape_invalid_shortcodes( $content );
return $content;
}
/**
@@ -294,6 +311,141 @@ function do_shortcode_tag( $m ) {
}
}
/**
* Search only inside HTML elements for shortcodes and process them.
*
* Any [ or ] characters remaining inside elements will be HTML encoded
* to prevent interference with shortcodes that are outside the elements.
* Assumes $content processed by KSES already. Users with unfiltered_html
* capability may get unexpected output if angle braces are nested in tags.
*
* @since 4.2.3
*
* @param string $content Content to search for shortcodes
* @param bool $ignore_html When true, all square braces inside elements will be encoded.
* @return string Content with shortcodes filtered out.
*/
function do_shortcodes_in_html_tags( $content, $ignore_html ) {
// Normalize entities in unfiltered HTML before adding placeholders.
$trans = array( '&#91;' => '&#091;', '&#93;' => '&#093;' );
$content = strtr( $content, $trans );
$trans = array( '[' => '&#91;', ']' => '&#93;' );
$pattern = get_shortcode_regex();
$comment_regex =
'!' // Start of comment, after the <.
. '(?:' // Unroll the loop: Consume everything until --> is found.
. '-(?!->)' // Dash not followed by end of comment.
. '[^\-]*+' // Consume non-dashes.
. ')*+' // Loop possessively.
. '(?:-->)?'; // End of comment. If not found, match all input.
$regex =
'/(' // Capture the entire match.
. '<' // Find start of element.
. '(?(?=!--)' // Is this a comment?
. $comment_regex // Find end of comment.
. '|'
. '[^>]*>?' // Find end of element. If not found, match all input.
. ')'
. ')/s';
$textarr = preg_split( $regex, $content, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
foreach ( $textarr as &$element ) {
if ( '<' !== $element[0] ) {
continue;
}
$noopen = false === strpos( $element, '[' );
$noclose = false === strpos( $element, ']' );
if ( $noopen || $noclose ) {
// This element does not contain shortcodes.
if ( $noopen xor $noclose ) {
// Need to encode stray [ or ] chars.
$element = strtr( $element, $trans );
}
continue;
}
if ( $ignore_html || '<!--' === substr( $element, 0, 4 ) ) {
// Encode all [ and ] chars.
$element = strtr( $element, $trans );
continue;
}
$attributes = wp_kses_attr_parse( $element );
if ( false === $attributes ) {
// Looks like we found some crazy unfiltered HTML. Skipping it for sanity.
$element = strtr( $element, $trans );
continue;
}
// Get element name
$front = array_shift( $attributes );
$back = array_pop( $attributes );
$matches = array();
preg_match('%[a-zA-Z0-9]+%', $front, $matches);
$elname = $matches[0];
// Look for shortcodes in each attribute separately.
foreach ( $attributes as &$attr ) {
$open = strpos( $attr, '[' );
$close = strpos( $attr, ']' );
if ( false === $open || false === $close ) {
continue; // Go to next attribute. Square braces will be escaped at end of loop.
}
$double = strpos( $attr, '"' );
$single = strpos( $attr, "'" );
if ( ( false === $single || $open < $single ) && ( false === $double || $open < $double ) ) {
// $attr like '[shortcode]' or 'name = [shortcode]' implies unfiltered_html.
// In this specific situation we assume KSES did not run because the input
// was written by an administrator, so we should avoid changing the output
// and we do not need to run KSES here.
$attr = preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $attr );
} else {
// $attr like 'name = "[shortcode]"' or "name = '[shortcode]'"
// We do not know if $content was unfiltered. Assume KSES ran before shortcodes.
$count = 0;
$new_attr = preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $attr, -1, $count );
if ( $count > 0 ) {
// Sanitize the shortcode output using KSES.
$new_attr = wp_kses_one_attr( $new_attr, $elname );
if ( '' !== $new_attr ) {
// The shortcode is safe to use now.
$attr = $new_attr;
}
}
}
}
$element = $front . implode( '', $attributes ) . $back;
// Now encode any remaining [ or ] chars.
$element = strtr( $element, $trans );
}
$content = implode( '', $textarr );
return $content;
}
/**
* Remove placeholders added by do_shortcodes_in_html_tags().
*
* @since 4.2.3
*
* @param string $content Content to search for placeholders.
* @return string Content with placeholders removed.
*/
function unescape_invalid_shortcodes( $content ) {
// Clean up entire string, avoids re-parsing HTML.
$trans = array( '&#91;' => '[', '&#93;' => ']' );
$content = strtr( $content, $trans );
return $content;
}
/**
* Retrieve all attributes from the shortcodes tag.
*
@@ -393,9 +545,15 @@ function strip_shortcodes( $content ) {
if (empty($shortcode_tags) || !is_array($shortcode_tags))
return $content;
$pattern = get_shortcode_regex();
$content = do_shortcodes_in_html_tags( $content, true );
return preg_replace_callback( "/$pattern/s", 'strip_shortcode_tag', $content );
$pattern = get_shortcode_regex();
$content = preg_replace_callback( "/$pattern/s", 'strip_shortcode_tag', $content );
// Always restore square braces so we don't break things like <!--[if IE ]>
$content = unescape_invalid_shortcodes( $content );
return $content;
}
function strip_shortcode_tag( $m ) {

View File

@@ -4,21 +4,21 @@
*
* @global string $wp_version
*/
$wp_version = '4.0.3';
$wp_version = '4.0.6';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
*
* @global int $wp_db_version
*/
$wp_db_version = 29630;
$wp_db_version = 29632;
/**
* Holds the TinyMCE version
*
* @global string $tinymce_version
*/
$tinymce_version = '4104-20140822';
$tinymce_version = '4104-20150505';
/**
* Holds the required PHP version

View File

@@ -1787,6 +1787,8 @@ class wpdb {
* @return int|false The number of rows affected, or false on error.
*/
function _insert_replace_helper( $table, $data, $format = null, $type = 'INSERT' ) {
$this->insert_id = 0;
if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) {
return false;
}
@@ -1807,7 +1809,6 @@ class wpdb {
$sql = "$type INTO `$table` ($fields) VALUES ($formats)";
$this->insert_id = 0;
$this->check_current_query = false;
return $this->query( $this->prepare( $sql, $values ) );
}
@@ -1928,11 +1929,20 @@ class wpdb {
*/
protected function process_fields( $table, $data, $format ) {
$data = $this->process_field_formats( $data, $format );
if ( false === $data ) {
return false;
}
$data = $this->process_field_charsets( $data, $table );
if ( false === $data ) {
return false;
}
$data = $this->process_field_lengths( $data, $table );
if ( false === $data ) {
return false;
}
$converted_data = $this->strip_invalid_text( $data );
if ( $data !== $converted_data ) {
@@ -1994,17 +2004,41 @@ class wpdb {
// We can skip this field if we know it isn't a string.
// This checks %d/%f versus ! %s because it's sprintf() could take more.
$value['charset'] = false;
} elseif ( $this->check_ascii( $value['value'] ) ) {
// If it's ASCII, then we don't need the charset. We can skip this field.
$value['charset'] = false;
} else {
$value['charset'] = $this->get_col_charset( $table, $field );
if ( is_wp_error( $value['charset'] ) ) {
return false;
}
}
// This isn't ASCII. Don't have strip_invalid_text() re-check.
$value['ascii'] = false;
$data[ $field ] = $value;
}
return $data;
}
/**
* For string fields, record the maximum string length that field can safely save.
*
* @since 4.2.1
* @access protected
*
* @param array $data As it comes from the wpdb::process_field_charsets() method.
* @param string $table Table name.
* @return array|False The same array as $data with additional 'length' keys, or false if
* any of the values were too long for their corresponding field.
*/
protected function process_field_lengths( $data, $table ) {
foreach ( $data as $field => $value ) {
if ( '%d' === $value['format'] || '%f' === $value['format'] ) {
// We can skip this field if we know it isn't a string.
// This checks %d/%f versus ! %s because it's sprintf() could take more.
$value['length'] = false;
} else {
$value['length'] = $this->get_col_length( $table, $field );
if ( is_wp_error( $value['length'] ) ) {
return false;
}
}
$data[ $field ] = $value;
@@ -2211,7 +2245,10 @@ class wpdb {
}
$charsets = $columns = array();
$results = $this->get_results( "SHOW FULL COLUMNS FROM `$table`" );
$table_parts = explode( '.', $table );
$table = '`' . implode( '`.`', $table_parts ) . '`';
$results = $this->get_results( "SHOW FULL COLUMNS FROM $table" );
if ( ! $results ) {
return new WP_Error( 'wpdb_get_table_charset_failure' );
}
@@ -2334,6 +2371,99 @@ class wpdb {
return $charset;
}
/**
* Retrieve the maximum string length allowed in a given column.
* The length may either be specified as a byte length or a character length.
*
* @since 4.2.1
* @access public
*
* @param string $table Table name.
* @param string $column Column name.
* @return mixed array( 'length' => (int), 'type' => 'byte' | 'char' )
* false if the column has no length (for example, numeric column)
* WP_Error object if there was an error.
*/
public function get_col_length( $table, $column ) {
$tablekey = strtolower( $table );
$columnkey = strtolower( $column );
// Skip this entirely if this isn't a MySQL database.
if ( false === $this->is_mysql ) {
return false;
}
if ( empty( $this->col_meta[ $tablekey ] ) ) {
// This primes column information for us.
$table_charset = $this->get_table_charset( $table );
if ( is_wp_error( $table_charset ) ) {
return $table_charset;
}
}
if ( empty( $this->col_meta[ $tablekey ][ $columnkey ] ) ) {
return false;
}
$typeinfo = explode( '(', $this->col_meta[ $tablekey ][ $columnkey ]->Type );
$type = strtolower( $typeinfo[0] );
if ( ! empty( $typeinfo[1] ) ) {
$length = trim( $typeinfo[1], ')' );
} else {
$length = false;
}
switch( $type ) {
case 'char':
case 'varchar':
return array(
'type' => 'char',
'length' => (int) $length,
);
break;
case 'binary':
case 'varbinary':
return array(
'type' => 'byte',
'length' => (int) $length,
);
break;
case 'tinyblob':
case 'tinytext':
return array(
'type' => 'byte',
'length' => 255, // 2^8 - 1
);
break;
case 'blob':
case 'text':
return array(
'type' => 'byte',
'length' => 65535, // 2^16 - 1
);
break;
case 'mediumblob':
case 'mediumtext':
return array(
'type' => 'byte',
'length' => 16777215, // 2^24 - 1
);
break;
case 'longblob':
case 'longtext':
return array(
'type' => 'byte',
'length' => 4294967295, // 2^32 - 1
);
break;
default:
return false;
}
return false;
}
/**
* Check if a string is ASCII.
*
@@ -2374,7 +2504,7 @@ class wpdb {
// We don't need to check the collation for queries that don't read data.
$query = ltrim( $query, "\r\n\t (" );
if ( preg_match( '/^(?:SHOW|DESCRIBE|DESC|EXPLAIN)\s/i', $query ) ) {
if ( preg_match( '/^(?:SHOW|DESCRIBE|DESC|EXPLAIN|CREATE)\s/i', $query ) ) {
return true;
}
@@ -2432,50 +2562,55 @@ class wpdb {
*/
// If any of the columns don't have one of these collations, it needs more sanity checking.
protected function strip_invalid_text( $data ) {
// Some multibyte character sets that we can check in PHP.
$mb_charsets = array(
'ascii' => 'ASCII',
'big5' => 'BIG-5',
'eucjpms' => 'eucJP-win',
'gb2312' => 'EUC-CN',
'ujis' => 'EUC-JP',
'utf32' => 'UTF-32',
);
$supported_charsets = array();
if ( function_exists( 'mb_list_encodings' ) ) {
$supported_charsets = mb_list_encodings();
}
$db_check_string = false;
foreach ( $data as &$value ) {
$charset = $value['charset'];
// Column isn't a string, or is latin1, which will will happily store anything.
if ( false === $charset || 'latin1' === $charset ) {
if ( is_array( $value['length'] ) ) {
$length = $value['length']['length'];
} else {
$length = false;
}
// There's no charset to work with.
if ( false === $charset ) {
continue;
}
// Column isn't a string.
if ( ! is_string( $value['value'] ) ) {
continue;
}
// ASCII is always OK.
if ( ! isset( $value['ascii'] ) && $this->check_ascii( $value['value'] ) ) {
continue;
$truncate_by_byte_length = 'byte' === $value['length']['type'];
$needs_validation = true;
if (
// latin1 can store any byte sequence
'latin1' === $charset
||
// ASCII is always OK.
( ! isset( $value['ascii'] ) && $this->check_ascii( $value['value'] ) )
) {
$truncate_by_byte_length = true;
$needs_validation = false;
}
// Convert the text locally.
if ( $supported_charsets ) {
if ( isset( $mb_charsets[ $charset ] ) && in_array( $mb_charsets[ $charset ], $supported_charsets ) ) {
$value['value'] = mb_convert_encoding( $value['value'], $mb_charsets[ $charset ], $mb_charsets[ $charset ] );
if ( $truncate_by_byte_length ) {
mbstring_binary_safe_encoding();
if ( false !== $length && strlen( $value['value'] ) > $length ) {
$value['value'] = substr( $value['value'], 0, $length );
}
reset_mbstring_encoding();
if ( ! $needs_validation ) {
continue;
}
}
// utf8 can be handled by regex, which is a bunch faster than a DB lookup.
if ( 'utf8' === $charset || 'utf8mb3' === $charset || 'utf8mb4' === $charset ) {
if ( ( 'utf8' === $charset || 'utf8mb3' === $charset || 'utf8mb4' === $charset ) && function_exists( 'mb_strlen' ) ) {
$regex = '/
(
(?: [\x00-\x7F] # single-byte sequences 0xxxxxxx
@@ -2485,7 +2620,7 @@ class wpdb {
| \xED[\x80-\x9F][\x80-\xBF]
| [\xEE-\xEF][\x80-\xBF]{2}';
if ( 'utf8mb4' === $charset) {
if ( 'utf8mb4' === $charset ) {
$regex .= '
| \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3
| [\xF1-\xF3][\x80-\xBF]{3}
@@ -2493,11 +2628,16 @@ class wpdb {
';
}
$regex .= '){1,50} # ...one or more times
$regex .= '){1,40} # ...one or more times
)
| . # anything else
/x';
$value['value'] = preg_replace( $regex, '$1', $value['value'] );
if ( false !== $length && mb_strlen( $value['value'], 'UTF-8' ) > $length ) {
$value['value'] = mb_substr( $value['value'], 0, $length, 'UTF-8' );
}
continue;
}
@@ -2514,8 +2654,14 @@ class wpdb {
$queries[ $value['charset'] ] = array();
}
// Split the CONVERT() calls by charset, so we can make sure the connection is right
$queries[ $value['charset'] ][ $col ] = $this->prepare( "CONVERT( %s USING {$value['charset']} )", $value['value'] );
// We're going to need to truncate by characters or bytes, depending on the length value we have.
if ( 'byte' === $value['length']['type'] ) {
// Split the CONVERT() calls by charset, so we can make sure the connection is right
$queries[ $value['charset'] ][ $col ] = $this->prepare( "CONVERT( LEFT( CONVERT( %s USING binary ), %d ) USING {$value['charset']} )", $value['value'], $value['length']['length'] );
} else {
$queries[ $value['charset'] ][ $col ] = $this->prepare( "LEFT( CONVERT( %s USING {$value['charset']} ), %d )", $value['value'], $value['length']['length'] );
}
unset( $data[ $col ]['db'] );
}
}
@@ -2534,16 +2680,19 @@ class wpdb {
$this->check_current_query = false;
$row = $this->get_row( "SELECT " . implode( ', ', $query ), ARRAY_N );
$sql = array();
foreach ( $query as $column => $column_query ) {
$sql[] = $column_query . " AS x_$column";
}
$row = $this->get_row( "SELECT " . implode( ', ', $sql ), ARRAY_A );
if ( ! $row ) {
$this->set_charset( $this->dbh, $connection_charset );
return new WP_Error( 'wpdb_strip_invalid_text_failure' );
}
$cols = array_keys( $query );
$col_count = count( $cols );
for ( $ii = 0; $ii < $col_count; $ii++ ) {
$data[ $cols[ $ii ] ]['value'] = $row[ $ii ];
foreach ( array_keys( $query ) as $column ) {
$data[ $column ]['value'] = $row["x_$column"];
}
}
@@ -2566,6 +2715,12 @@ class wpdb {
* @return string|WP_Error The converted query, or a {@see WP_Error} object if the conversion fails.
*/
protected function strip_invalid_text_from_query( $query ) {
// We don't need to check the collation for queries that don't read data.
$trimmed_query = ltrim( $query, "\r\n\t (" );
if ( preg_match( '/^(?:SHOW|DESCRIBE|DESC|EXPLAIN|CREATE)\s/i', $trimmed_query ) ) {
return $query;
}
$table = $this->get_table_from_query( $query );
if ( $table ) {
$charset = $this->get_table_charset( $table );
@@ -2585,6 +2740,7 @@ class wpdb {
'value' => $query,
'charset' => $charset,
'ascii' => false,
'length' => false,
);
$data = $this->strip_invalid_text( array( $data ) );
@@ -2607,7 +2763,7 @@ class wpdb {
* @return string|WP_Error The converted string, or a `WP_Error` object if the conversion fails.
*/
public function strip_invalid_text_for_column( $table, $column, $value ) {
if ( ! is_string( $value ) || $this->check_ascii( $value ) ) {
if ( ! is_string( $value ) ) {
return $value;
}
@@ -2624,7 +2780,7 @@ class wpdb {
$column => array(
'value' => $value,
'charset' => $charset,
'ascii' => false,
'length' => $this->get_col_length( $table, $column ),
)
);
@@ -2665,16 +2821,16 @@ class wpdb {
. '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?'
. '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?'
. '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?'
. ')\s+`?([\w-]+)`?/is', $query, $maybe ) ) {
return $maybe[1];
. ')\s+((?:[0-9a-zA-Z$_.`]|[\xC2-\xDF][\x80-\xBF])+)/is', $query, $maybe ) ) {
return str_replace( '`', '', $maybe[1] );
}
// SHOW TABLE STATUS and SHOW TABLES
if ( preg_match( '/^\s*(?:'
. 'SHOW\s+TABLE\s+STATUS.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)'
. '|SHOW\s+(?:FULL\s+)?TABLES.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)'
. ')\W([\w-]+)\W/is', $query, $maybe ) ) {
return $maybe[1];
. ')\W((?:[0-9a-zA-Z$_.`]|[\xC2-\xDF][\x80-\xBF])+)\W/is', $query, $maybe ) ) {
return str_replace( '`', '', $maybe[1] );
}
// Big pattern for the rest of the table-related queries.
@@ -2692,8 +2848,8 @@ class wpdb {
. '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE'
. '|(?:GRANT|REVOKE).*ON\s+TABLE'
. '|SHOW\s+(?:.*FROM|.*TABLE)'
. ')\s+\(*\s*`?([\w-]+)`?\s*\)*/is', $query, $maybe ) ) {
return $maybe[1];
. ')\s+\(*\s*((?:[0-9a-zA-Z$_.`]|[\xC2-\xDF][\x80-\xBF])+)\s*\)*/is', $query, $maybe ) ) {
return str_replace( '`', '', $maybe[1] );
}
return false;