From 0461a5f2e55c8d5f1fde96ca2e83117152573c7d Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Sun, 10 Aug 2014 02:27:54 +0100 Subject: [PATCH] WordPress 3.9.2 Signed-off-by: Edward Z. Yang --- readme.html | 2 +- wp-admin/about.php | 6 +- wp-content/plugins/akismet/.htaccess | 11 + wp-content/plugins/akismet/_inc/akismet.css | 4 +- wp-content/plugins/akismet/_inc/akismet.js | 2 +- wp-content/plugins/akismet/_inc/form.js | 41 ++- wp-content/plugins/akismet/akismet.php | 6 +- .../plugins/akismet/class.akismet-admin.php | 281 ++++++------------ wp-content/plugins/akismet/class.akismet.php | 175 ++++++++++- wp-content/plugins/akismet/readme.txt | 34 ++- wp-content/plugins/akismet/views/notice.php | 2 +- wp-content/plugins/akismet/wrapper.php | 16 +- wp-includes/ID3/getid3.lib.php | 11 +- wp-includes/class-IXR.php | 32 +- wp-includes/class-wp-customize-widgets.php | 28 +- wp-includes/compat.php | 29 ++ wp-includes/pluggable.php | 49 ++- wp-includes/version.php | 2 +- wp-login.php | 26 +- 19 files changed, 478 insertions(+), 279 deletions(-) create mode 100644 wp-content/plugins/akismet/.htaccess diff --git a/readme.html b/readme.html index faad3e9b..914fb2a4 100644 --- a/readme.html +++ b/readme.html @@ -9,7 +9,7 @@

WordPress -
Version 3.9.1 +
Version 3.9.2

Semantic Personal Publishing Platform

diff --git a/wp-admin/about.php b/wp-admin/about.php index 0a4344b1..f5c78601 100644 --- a/wp-admin/about.php +++ b/wp-admin/about.php @@ -39,7 +39,11 @@ include( ABSPATH . 'wp-admin/admin-header.php' );
-

+

+

Version %1$s addressed a security issue.', + 'Version %1$s addressed some security issues.', 6 ), '3.9.2', number_format_i18n( 6 ) ); ?> + the release notes.' ), 'http://codex.wordpress.org/Version_3.9.2' ); ?> +

Version %1$s addressed %2$s bug.', 'Version %1$s addressed %2$s bugs.', 34 ), '3.9.1', number_format_i18n( 34 ) ); ?> the release notes.' ), 'http://codex.wordpress.org/Version_3.9.1' ); ?> diff --git a/wp-content/plugins/akismet/.htaccess b/wp-content/plugins/akismet/.htaccess new file mode 100644 index 00000000..6d8b4ec2 --- /dev/null +++ b/wp-content/plugins/akismet/.htaccess @@ -0,0 +1,11 @@ +Order Deny,Allow +Deny from all + + + Allow from all + + +#allow access to any image + + Allow from all + \ No newline at end of file diff --git a/wp-content/plugins/akismet/_inc/akismet.css b/wp-content/plugins/akismet/_inc/akismet.css index e1d6fd53..ff076aab 100644 --- a/wp-content/plugins/akismet/_inc/akismet.css +++ b/wp-content/plugins/akismet/_inc/akismet.css @@ -75,8 +75,8 @@ h2.ak-header { display: inline-block !important; } .checkforspam-spinner { - display: none; - margin-top: 10px; + display: inline-block; + margin-top: 7px; } .config-wrap { diff --git a/wp-content/plugins/akismet/_inc/akismet.js b/wp-content/plugins/akismet/_inc/akismet.js index ed65220c..aa0aab42 100644 --- a/wp-content/plugins/akismet/_inc/akismet.js +++ b/wp-content/plugins/akismet/_inc/akismet.js @@ -125,7 +125,7 @@ jQuery( function ( $ ) { }); $('.checkforspam:not(.button-disabled)').click( function(e) { $('.checkforspam:not(.button-disabled)').addClass('button-disabled'); - $('.checkforspam-spinner').show(); + $('.checkforspam-spinner').addClass( 'spinner' ); akismet_check_for_spam(0, 100); e.preventDefault(); }); diff --git a/wp-content/plugins/akismet/_inc/form.js b/wp-content/plugins/akismet/_inc/form.js index ec5e56e7..3a5be8af 100644 --- a/wp-content/plugins/akismet/_inc/form.js +++ b/wp-content/plugins/akismet/_inc/form.js @@ -1,17 +1,30 @@ -jQuery( function ( $ ) { - var ak_js = $( '#ak_js' ); +var ak_js = document.getElementById( "ak_js" ); - // If the form field already exists just use that - if ( ak_js.length == 0 ) { - ak_js = $( '' ); - } - else { - ak_js.remove(); - } +if ( ! ak_js ) { + ak_js = document.createElement( 'input' ); + ak_js.setAttribute( 'id', 'ak_js' ); + ak_js.setAttribute( 'name', 'ak_js' ); + ak_js.setAttribute( 'type', 'hidden' ); +} +else { + ak_js.parentNode.removeChild( ak_js ); +} + +ak_js.setAttribute( 'value', ( new Date() ).getTime() ); - ak_js.val( ( new Date() ).getTime() ); +var commentForm = document.getElementById( 'commentform' ); - // single page, front-end comment form - // inline comment reply, wp-admin - $( '#commentform, #replyrow td:first' ).append( ak_js ); -} ); +if ( commentForm ) { + commentForm.appendChild( ak_js ); +} +else { + var replyRowContainer = document.getElementById( 'replyrow' ); + + if ( replyRowContainer ) { + var children = replyRowContainer.getElementsByTagName( 'td' ); + + if ( children.length > 0 ) { + children[0].appendChild( ak_js ); + } + } +} \ No newline at end of file diff --git a/wp-content/plugins/akismet/akismet.php b/wp-content/plugins/akismet/akismet.php index 811ab717..7fc1e9db 100644 --- a/wp-content/plugins/akismet/akismet.php +++ b/wp-content/plugins/akismet/akismet.php @@ -6,7 +6,7 @@ Plugin Name: Akismet Plugin URI: http://akismet.com/ Description: Used by millions, Akismet is quite possibly the best way in the world to protect your blog from comment and trackback spam. It keeps your site protected from spam even while you sleep. To get started: 1) Click the "Activate" link to the left of this description, 2) Sign up for an Akismet API key, and 3) Go to your Akismet configuration page, and save your API key. -Version: 3.0.0 +Version: 3.0.1 Author: Automattic Author URI: http://automattic.com/wordpress-plugins/ License: GPLv2 or later @@ -35,8 +35,8 @@ if ( !function_exists( 'add_action' ) ) { exit; } -define( 'AKISMET_VERSION', '3.0.0' ); -define( 'AKISMET__MINIMUM_WP_VERSION', '3.0' ); +define( 'AKISMET_VERSION', '3.0.1' ); +define( 'AKISMET__MINIMUM_WP_VERSION', '3.1' ); define( 'AKISMET__PLUGIN_URL', plugin_dir_url( __FILE__ ) ); define( 'AKISMET__PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); define( 'AKISMET_DELETE_LIMIT', 100000 ); diff --git a/wp-content/plugins/akismet/class.akismet-admin.php b/wp-content/plugins/akismet/class.akismet-admin.php index 15f5d479..7a62ccbd 100644 --- a/wp-content/plugins/akismet/class.akismet-admin.php +++ b/wp-content/plugins/akismet/class.akismet-admin.php @@ -33,7 +33,6 @@ class Akismet_Admin { add_action( 'activity_box_end', array( 'Akismet_Admin', 'dashboard_stats' ) ); add_action( 'rightnow_end', array( 'Akismet_Admin', 'rightnow_stats' ) ); add_action( 'manage_comments_nav', array( 'Akismet_Admin', 'check_for_spam_button' ) ); - add_action( 'transition_comment_status', array( 'Akismet_Admin', 'transition_comment_status' ), 10, 3 ); add_action( 'admin_action_akismet_recheck_queue', array( 'Akismet_Admin', 'recheck_queue' ) ); add_action( 'wp_ajax_akismet_recheck_queue', array( 'Akismet_Admin', 'recheck_queue' ) ); add_action( 'wp_ajax_comment_author_deurl', array( 'Akismet_Admin', 'remove_comment_author_url' ) ); @@ -44,6 +43,8 @@ class Akismet_Admin { add_filter( 'comment_text', array( 'Akismet_Admin', 'text_add_link_class' ) ); add_filter( 'plugin_action_links_'.plugin_basename( plugin_dir_path( __FILE__ ) . 'akismet.php'), array( 'Akismet_Admin', 'admin_plugin_settings_link' ) ); + + add_filter( 'wxr_export_skip_commentmeta', array( 'Akismet_Admin', 'exclude_commentmeta_from_export' ), 10, 3 ); } public static function admin_init() { @@ -64,7 +65,7 @@ class Akismet_Admin { } public static function admin_plugin_settings_link( $links ) { - $settings_link = ''.__('Settings', 'akismet').''; + $settings_link = ''.__('Settings', 'akismet').''; array_unshift( $links, $settings_link ); return $links; } @@ -331,51 +332,7 @@ class Akismet_Admin { else $link = add_query_arg( array( 'page' => 'akismet-admin', 'recheckqueue' => 'true', 'noheader' => 'true' ), admin_url( 'edit-comments.php' ) ); - echo '

' . esc_html__('Check for Spam', 'akismet') . ''; - echo ''; - } - - public static function transition_comment_status( $new_status, $old_status, $comment ) { - if ( $new_status == $old_status ) - return; - - # we don't need to record a history item for deleted comments - if ( $new_status == 'delete' ) - return; - - if ( !is_admin() ) - return; - - if ( !current_user_can( 'edit_post', $comment->comment_post_ID ) && !current_user_can( 'moderate_comments' ) ) - return; - - if ( defined('WP_IMPORTING') && WP_IMPORTING == true ) - return; - - // if this is present, it means the status has been changed by a re-check, not an explicit user action - if ( get_comment_meta( $comment->comment_ID, 'akismet_rechecking' ) ) - return; - - global $current_user; - $reporter = ''; - if ( is_object( $current_user ) ) - $reporter = $current_user->user_login; - - // Assumption alert: - // We want to submit comments to Akismet only when a moderator explicitly spams or approves it - not if the status - // is changed automatically by another plugin. Unfortunately WordPress doesn't provide an unambiguous way to - // determine why the transition_comment_status action was triggered. And there are several different ways by which - // to spam and unspam comments: bulk actions, ajax, links in moderation emails, the dashboard, and perhaps others. - // We'll assume that this is an explicit user action if POST or GET has an 'action' key. - if ( isset($_POST['action']) || isset($_GET['action']) ) { - if ( $new_status == 'spam' && ( $old_status == 'approved' || $old_status == 'unapproved' || !$old_status ) ) { - return self::submit_spam_comment( $comment->comment_ID ); - } elseif ( $old_status == 'spam' && ( $new_status == 'approved' || $new_status == 'unapproved' ) ) { - return self::submit_nonspam_comment( $comment->comment_ID ); - } - } - - Akismet::update_comment_history( $comment->comment_ID, sprintf( __('%1$s changed the comment status to %2$s', 'akismet'), $reporter, $new_status ), 'status-' . $new_status ); + echo '
' . esc_html__('Check for Spam', 'akismet') . ''; } public static function recheck_queue() { @@ -410,7 +367,7 @@ class Akismet_Admin { add_comment_meta( $c['comment_ID'], 'akismet_rechecking', true ); - $response = Akismet::http_post( http_build_query( $c ), 'comment-check' ); + $response = Akismet::http_post( build_query( $c ), 'comment-check' ); if ( 'true' == $response[1] ) { wp_set_comment_status( $c['comment_ID'], 'spam' ); update_comment_meta( $c['comment_ID'], 'akismet_result', 'true' ); @@ -514,7 +471,10 @@ class Akismet_Admin { if ( $desc ) echo ''.esc_html( $desc ).''; - if ( apply_filters( 'akismet_show_user_comments_approved', get_option('akismet_show_user_comments_approved') ) ) { + $show_user_comments = apply_filters( 'akismet_show_user_comments_approved', get_option('akismet_show_user_comments_approved') ); + $show_user_comments = $show_user_comments === 'false' ? false : $show_user_comments; //option used to be saved as 'false' / 'true' + + if ( $show_user_comments ) { $comment_count = Akismet::get_user_comments_approved( $comment->user_id, $comment->comment_author_email, $comment->comment_author, $comment->comment_author_url ); $comment_count = intval( $comment_count ); echo ''; @@ -557,102 +517,6 @@ class Akismet_Admin { return preg_replace_callback( '#]*)href="([^"]+)"([^>]*)>(.*?)#i', array( 'Akismet_Admin', 'text_add_link_callback' ), $comment_text ); } - public static function submit_spam_comment( $comment_id ) { - global $wpdb, $current_user, $current_site; - - $comment_id = (int) $comment_id; - - $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) ); - - if ( !$comment ) // it was deleted - return; - - if ( 'spam' != $comment->comment_approved ) - return; - - // use the original version stored in comment_meta if available - $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true); - - if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) - $comment = (object) array_merge( (array)$comment, $as_submitted ); - - $comment->blog = get_bloginfo('url'); - $comment->blog_lang = get_locale(); - $comment->blog_charset = get_option('blog_charset'); - $comment->permalink = get_permalink($comment->comment_post_ID); - - if ( is_object($current_user) ) - $comment->reporter = $current_user->user_login; - - if ( is_object($current_site) ) - $comment->site_domain = $current_site->domain; - - $comment->user_role = ''; - if ( isset( $comment->user_ID ) ) - $comment->user_role = Akismet::get_user_roles( $comment->user_ID ); - - if ( Akismet::is_test_mode() ) - $comment->is_test = 'true'; - - $post = get_post( $comment->comment_post_ID ); - $comment->comment_post_modified_gmt = $post->post_modified_gmt; - - $response = Akismet::http_post( http_build_query( $comment ), 'submit-spam' ); - if ( $comment->reporter ) { - Akismet::update_comment_history( $comment_id, sprintf( __('%s reported this comment as spam', 'akismet'), $comment->reporter ), 'report-spam' ); - update_comment_meta( $comment_id, 'akismet_user_result', 'true' ); - update_comment_meta( $comment_id, 'akismet_user', $comment->reporter ); - } - - do_action('akismet_submit_spam_comment', $comment_id, $response[1]); - } - - public static function submit_nonspam_comment( $comment_id ) { - global $wpdb, $current_user, $current_site; - - $comment_id = (int) $comment_id; - - $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) ); - if ( !$comment ) // it was deleted - return; - - // use the original version stored in comment_meta if available - $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true); - - if ( $as_submitted && is_array($as_submitted) && isset($as_submitted['comment_content']) ) - $comment = (object) array_merge( (array)$comment, $as_submitted ); - - $comment->blog = get_bloginfo('url'); - $comment->blog_lang = get_locale(); - $comment->blog_charset = get_option('blog_charset'); - $comment->permalink = get_permalink( $comment->comment_post_ID ); - $comment->user_role = ''; - - if ( is_object($current_user) ) - $comment->reporter = $current_user->user_login; - - if ( is_object($current_site) ) - $comment->site_domain = $current_site->domain; - - if ( isset( $comment->user_ID ) ) - $comment->user_role = Akismet::get_user_roles($comment->user_ID); - - if ( Akismet::is_test_mode() ) - $comment->is_test = 'true'; - - $post = get_post( $comment->comment_post_ID ); - $comment->comment_post_modified_gmt = $post->post_modified_gmt; - - $response = Akismet::http_post( http_build_query( $comment ), 'submit-ham' ); - if ( $comment->reporter ) { - Akismet::update_comment_history( $comment_id, sprintf( __('%s reported this comment as not spam', 'akismet'), $comment->reporter ), 'report-ham' ); - update_comment_meta( $comment_id, 'akismet_user_result', 'false' ); - update_comment_meta( $comment_id, 'akismet_user', $comment->reporter ); - } - - do_action('akismet_submit_nonspam_comment', $comment_id, $response[1]); - } - // Total spam in queue // get_option( 'akismet_spam_count' ) is the total caught ever public static function get_spam_count( $type = false ) { @@ -672,53 +536,73 @@ class Akismet_Admin { return $count; } elseif ( 'comments' == $type || 'comment' == $type ) { // comments $type = ''; - } else { // pingback, trackback, ... - $type = $wpdb->escape( $type ); } - return (int) $wpdb->get_var("SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam' AND comment_type='$type'"); + return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam' AND comment_type = %s", $type ) ); } // Check connectivity between the WordPress blog and Akismet's servers. // Returns an associative array of server IP addresses, where the key is the IP address, and value is true (available) or false (unable to connect). - public static function check_server_connectivity() { - $test_host = 'rest.akismet.com'; - - // Some web hosts may disable one or both functions - if ( !function_exists('fsockopen') || !function_exists('gethostbynamel') ) - return array(); - - $ips = gethostbynamel( $test_host ); - if ( !$ips || !is_array($ips) || !count($ips) ) - return array(); - - $api_key = Akismet::get_api_key(); + public static function check_server_ip_connectivity() { + + $servers = $ips = array(); - $servers = array(); - foreach ( $ips as $ip ) { - $response = Akismet::verify_key( $api_key, $ip ); - // even if the key is invalid, at least we know we have connectivity - if ( $response == 'valid' || $response == 'invalid' ) - $servers[$ip] = true; - else - $servers[$ip] = false; + // Some web hosts may disable this function + if ( function_exists('gethostbynamel') ) { + + $ips = gethostbynamel( 'rest.akismet.com' ); + if ( $ips && is_array($ips) && count($ips) ) { + $api_key = Akismet::get_api_key(); + + foreach ( $ips as $ip ) { + $response = Akismet::verify_key( $api_key, $ip ); + // even if the key is invalid, at least we know we have connectivity + if ( $response == 'valid' || $response == 'invalid' ) + $servers[$ip] = 'connected'; + else + $servers[$ip] = $response ? $response : 'unable to connect'; + } + } } + return $servers; } - - // Check the server connectivity and store the results in an option. - // Cached results will be used if not older than the specified timeout in seconds; use $cache_timeout = 0 to force an update. - // Returns the same associative array as check_server_connectivity() - public static function get_server_connectivity( $cache_timeout = 86400 ) { + + // Simpler connectivity check + public static function check_server_connectivity($cache_timeout = 86400) { + + $debug = array(); + $debug[ 'PHP_VERSION' ] = PHP_VERSION; + $debug[ 'WORDPRESS_VERSION' ] = $GLOBALS['wp_version']; + $debug[ 'AKISMET_VERSION' ] = AKISMET_VERSION; + $debug[ 'AKISMET__PLUGIN_DIR' ] = AKISMET__PLUGIN_DIR; + $debug[ 'SITE_URL' ] = site_url(); + $debug[ 'HOME_URL' ] = home_url(); + $servers = get_option('akismet_available_servers'); - if ( (time() - get_option('akismet_connectivity_time') < $cache_timeout) && $servers !== false ) - return $servers; + if ( (time() - get_option('akismet_connectivity_time') < $cache_timeout) && $servers !== false ) { + $servers = self::check_server_ip_connectivity(); + update_option('akismet_available_servers', $servers); + update_option('akismet_connectivity_time', time()); + } + + $response = wp_remote_get( 'http://rest.akismet.com/1.1/test' ); + + $debug[ 'gethostbynamel' ] = function_exists('gethostbynamel') ? 'exists' : 'not here'; + $debug[ 'Servers' ] = $servers; + $debug[ 'Test Connection' ] = $response; + + Akismet::log( $debug ); + + if ( $response && 'connected' == wp_remote_retrieve_body( $response ) ) + return true; + + return false; + } - // There's a race condition here but the effect is harmless. - $servers = self::check_server_connectivity(); - update_option('akismet_available_servers', $servers); - update_option('akismet_connectivity_time', time()); - return $servers; + // Check the server connectivity and store the available servers in an option. + public static function get_server_connectivity($cache_timeout = 86400) { + return self::check_server_connectivity( $cache_timeout ); } public static function get_number_spam_waiting() { @@ -741,7 +625,7 @@ class Akismet_Admin { } public static function get_akismet_user( $api_key ) { - $akismet_user = Akismet::http_post( http_build_query( array( 'key' => $api_key ) ), 'get-subscription' ); + $akismet_user = Akismet::http_post( build_query( array( 'key' => $api_key ) ), 'get-subscription' ); if ( ! empty( $akismet_user[1] ) ) $akismet_user = json_decode( $akismet_user[1] ); @@ -755,7 +639,7 @@ class Akismet_Admin { $stat_totals = array(); foreach( array( '6-months', 'all' ) as $interval ) { - $response = Akismet::http_post( http_build_query( array( 'blog' => urlencode( get_bloginfo('url') ), 'key' => $api_key, 'from' => $interval ) ), 'get-stats' ); + $response = Akismet::http_post( build_query( array( 'blog' => urlencode( get_bloginfo('url') ), 'key' => $api_key, 'from' => $interval ) ), 'get-stats' ); if ( ! empty( $response[1] ) ) { $stat_totals[$interval] = json_decode( $response[1] ); @@ -765,7 +649,7 @@ class Akismet_Admin { } public static function verify_wpcom_key( $api_key, $user_id, $token = '' ) { - $akismet_account = Akismet::http_post( http_build_query( array( + $akismet_account = Akismet::http_post( build_query( array( 'user_id' => $user_id, 'api_key' => $api_key, 'token' => $token, @@ -790,9 +674,11 @@ class Akismet_Admin { public static function display_spam_check_warning() { Akismet::fix_scheduled_recheck(); + + $link_text = apply_filters( 'akismet_spam_check_warning_link_text', sprintf( __( 'Please check your Akismet configuration and contact your web host if problems persist.', 'akismet'), esc_url( self::get_page_url() ) ) ); if ( self::get_number_spam_waiting() > 0 && wp_next_scheduled('akismet_schedule_cron_recheck') > time() ) - Akismet::view( 'notice', array( 'type' => 'spam-check' ) ); + Akismet::view( 'notice', array( 'type' => 'spam-check', 'link_text' => $link_text ) ); } public static function display_invalid_version() { @@ -869,7 +755,7 @@ class Akismet_Admin { if ( empty( self::$notices ) ) { //show status - if ( $akismet_user->status == 'active' && $akismet_user->account_type == 'free-api-key' ) { + if ( ! empty( $stat_totals['all'] ) && isset( $stat_totals['all']->time_saved ) && $akismet_user->status == 'active' && $akismet_user->account_type == 'free-api-key' ) { $time_saved = false; @@ -920,16 +806,11 @@ class Akismet_Admin { } public static function display_status() { - $servers = self::get_server_connectivity(); - $fail_count = count( $servers ) - count( array_filter( $servers ) ); - $type = ''; + $type = ''; - if ( empty( $servers ) || $fail_count > 0 ) + if ( !self::get_server_connectivity() ) $type = 'servers-be-down'; - if ( !function_exists('fsockopen') || !function_exists('gethostbynamel') ) - $type = 'missing-functions'; - if ( !empty( $type ) ) Akismet::view( 'notice', compact( 'type' ) ); elseif ( !empty( self::$notices ) ) { @@ -961,4 +842,20 @@ class Akismet_Admin { } return false; } + + /** + * Some commentmeta isn't useful in an export file. Suppress it (when supported). + * + * @param bool $exclude + * @param string $key The meta key + * @param object $meta The meta object + * @return bool Whether to exclude this meta entry from the export. + */ + public static function exclude_commentmeta_from_export( $exclude, $key, $meta ) { + if ( in_array( $key, array( 'akismet_as_submitted', 'akismet_rechecking', 'akismet_delayed_moderation_email' ) ) ) { + return true; + } + + return $exclude; + } } \ No newline at end of file diff --git a/wp-content/plugins/akismet/class.akismet.php b/wp-content/plugins/akismet/class.akismet.php index 7cf813d8..17f6b005 100644 --- a/wp-content/plugins/akismet/class.akismet.php +++ b/wp-content/plugins/akismet/class.akismet.php @@ -33,12 +33,14 @@ class Akismet { if ( $akismet_comment_nonce_option == 'true' || $akismet_comment_nonce_option == '' ) add_action( 'comment_form', array( 'Akismet', 'add_comment_nonce' ), 1 ); - add_action( 'admin_footer-edit-comments.php', array( 'Akismet', 'load_form_js' ) ); + add_action( 'admin_head-edit-comments.php', array( 'Akismet', 'load_form_js' ) ); add_action( 'comment_form', array( 'Akismet', 'load_form_js' ) ); add_action( 'comment_form', array( 'Akismet', 'inject_ak_js' ) ); add_filter( 'comment_moderation_recipients', array( 'Akismet', 'disable_moderation_emails_if_unreachable' ), 1000, 2 ); add_filter( 'pre_comment_approved', array( 'Akismet', 'last_comment_status' ), 10, 2 ); + + add_action( 'transition_comment_status', array( 'Akismet', 'transition_comment_status' ), 10, 3 ); if ( '3.0.5' == $GLOBALS['wp_version'] ) { remove_filter( 'comment_text', 'wp_kses_data' ); @@ -48,11 +50,11 @@ class Akismet { } public static function get_api_key() { - return defined('WPCOM_API_KEY') ? constant('WPCOM_API_KEY') : get_option('wordpress_api_key'); + return apply_filters( 'akismet_get_api_key', defined('WPCOM_API_KEY') ? constant('WPCOM_API_KEY') : get_option('wordpress_api_key') ); } public static function check_key_status( $key, $ip = null ) { - return self::http_post( http_build_query( array( 'key' => $key, 'blog' => get_option('home') ) ), 'verify-key', $ip ); + return self::http_post( build_query( array( 'key' => $key, 'blog' => get_option('home') ) ), 'verify-key', $ip ); } public static function verify_key( $key, $ip = null ) { @@ -115,19 +117,24 @@ class Akismet { $post = get_post( $comment['comment_post_ID'] ); $comment[ 'comment_post_modified_gmt' ] = $post->post_modified_gmt; - $response = self::http_post( http_build_query( $comment ), 'comment-check' ); + $response = self::http_post( build_query( $comment ), 'comment-check' ); do_action( 'akismet_comment_check_response', $response ); self::update_alert( $response ); - $commentdata['comment_as_submitted'] = $comment; + $commentdata['comment_as_submitted'] = array_intersect_key( $comment, array( 'blog' => '', 'blog_charset' => '', 'blog_lang' => '', 'blog_ua' => '', 'comment_agent' => '', 'comment_author' => '', 'comment_author_IP' => '', 'comment_author_email' => '', 'comment_author_url' => '', 'comment_content' => '', 'comment_date_gmt' => '', 'comment_tags' => '', 'comment_type' => '', 'guid' => '', 'is_test' => '', 'permalink' => '', 'reporter' => '', 'site_domain' => '', 'submit_referer' => '', 'submit_uri' => '', 'user_ID' => '', 'user_agent' => '', 'user_id' => '', 'user_ip' => '' ) ); $commentdata['akismet_result'] = $response[1]; if ( isset( $response[0]['x-akismet-pro-tip'] ) ) $commentdata['akismet_pro_tip'] = $response[0]['x-akismet-pro-tip']; - if ( 'true' == $response[1] ) { + if ( isset( $response[0]['x-akismet-error'] ) ) { + // An error occurred that we anticipated (like a suspended key) and want the user to act on. + // Send to moderation. + self::$last_comment_result = '0'; + } + else if ( 'true' == $response[1] ) { // akismet_spam_count will be incremented later by comment_is_spam() self::$last_comment_result = 'spam'; @@ -363,10 +370,155 @@ class Akismet { if ( self::is_test_mode() ) $c['is_test'] = 'true'; - $response = self::http_post( http_build_query( $c ), 'comment-check' ); + $response = self::http_post( build_query( $c ), 'comment-check' ); return ( is_array( $response ) && ! empty( $response[1] ) ) ? $response[1] : false; } + + + + public static function transition_comment_status( $new_status, $old_status, $comment ) { + + if ( $new_status == $old_status ) + return; + + # we don't need to record a history item for deleted comments + if ( $new_status == 'delete' ) + return; + + if ( !current_user_can( 'edit_post', $comment->comment_post_ID ) && !current_user_can( 'moderate_comments' ) ) + return; + + if ( defined('WP_IMPORTING') && WP_IMPORTING == true ) + return; + + // if this is present, it means the status has been changed by a re-check, not an explicit user action + if ( get_comment_meta( $comment->comment_ID, 'akismet_rechecking' ) ) + return; + + global $current_user; + $reporter = ''; + if ( is_object( $current_user ) ) + $reporter = $current_user->user_login; + + // Assumption alert: + // We want to submit comments to Akismet only when a moderator explicitly spams or approves it - not if the status + // is changed automatically by another plugin. Unfortunately WordPress doesn't provide an unambiguous way to + // determine why the transition_comment_status action was triggered. And there are several different ways by which + // to spam and unspam comments: bulk actions, ajax, links in moderation emails, the dashboard, and perhaps others. + // We'll assume that this is an explicit user action if certain POST/GET variables exist. + if ( ( isset( $_POST['status'] ) && in_array( $_POST['status'], array( 'spam', 'unspam' ) ) ) || + ( isset( $_POST['spam'] ) && (int) $_POST['spam'] == 1 ) || + ( isset( $_POST['unspam'] ) && (int) $_POST['unspam'] == 1 ) || + ( isset( $_POST['comment_status'] ) && in_array( $_POST['comment_status'], array( 'spam', 'unspam' ) ) ) || + ( isset( $_GET['action'] ) && in_array( $_GET['action'], array( 'spam', 'unspam' ) ) ) || + ( isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'editedcomment' ) ) ) + ) { + if ( $new_status == 'spam' && ( $old_status == 'approved' || $old_status == 'unapproved' || !$old_status ) ) { + return self::submit_spam_comment( $comment->comment_ID ); + } elseif ( $old_status == 'spam' && ( $new_status == 'approved' || $new_status == 'unapproved' ) ) { + return self::submit_nonspam_comment( $comment->comment_ID ); + } + } + + self::update_comment_history( $comment->comment_ID, sprintf( __('%1$s changed the comment status to %2$s', 'akismet'), $reporter, $new_status ), 'status-' . $new_status ); + } + + public static function submit_spam_comment( $comment_id ) { + global $wpdb, $current_user, $current_site; + + $comment_id = (int) $comment_id; + + $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) ); + + if ( !$comment ) // it was deleted + return; + + if ( 'spam' != $comment->comment_approved ) + return; + + // use the original version stored in comment_meta if available + $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true); + + if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) + $comment = (object) array_merge( (array)$comment, $as_submitted ); + + $comment->blog = get_bloginfo('url'); + $comment->blog_lang = get_locale(); + $comment->blog_charset = get_option('blog_charset'); + $comment->permalink = get_permalink($comment->comment_post_ID); + + if ( is_object($current_user) ) + $comment->reporter = $current_user->user_login; + + if ( is_object($current_site) ) + $comment->site_domain = $current_site->domain; + + $comment->user_role = ''; + if ( isset( $comment->user_ID ) ) + $comment->user_role = Akismet::get_user_roles( $comment->user_ID ); + + if ( self::is_test_mode() ) + $comment->is_test = 'true'; + + $post = get_post( $comment->comment_post_ID ); + $comment->comment_post_modified_gmt = $post->post_modified_gmt; + + $response = Akismet::http_post( build_query( $comment ), 'submit-spam' ); + if ( $comment->reporter ) { + self::update_comment_history( $comment_id, sprintf( __('%s reported this comment as spam', 'akismet'), $comment->reporter ), 'report-spam' ); + update_comment_meta( $comment_id, 'akismet_user_result', 'true' ); + update_comment_meta( $comment_id, 'akismet_user', $comment->reporter ); + } + + do_action('akismet_submit_spam_comment', $comment_id, $response[1]); + } + + public static function submit_nonspam_comment( $comment_id ) { + global $wpdb, $current_user, $current_site; + + $comment_id = (int) $comment_id; + + $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) ); + if ( !$comment ) // it was deleted + return; + + // use the original version stored in comment_meta if available + $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true); + + if ( $as_submitted && is_array($as_submitted) && isset($as_submitted['comment_content']) ) + $comment = (object) array_merge( (array)$comment, $as_submitted ); + + $comment->blog = get_bloginfo('url'); + $comment->blog_lang = get_locale(); + $comment->blog_charset = get_option('blog_charset'); + $comment->permalink = get_permalink( $comment->comment_post_ID ); + $comment->user_role = ''; + + if ( is_object($current_user) ) + $comment->reporter = $current_user->user_login; + + if ( is_object($current_site) ) + $comment->site_domain = $current_site->domain; + + if ( isset( $comment->user_ID ) ) + $comment->user_role = Akismet::get_user_roles($comment->user_ID); + + if ( Akismet::is_test_mode() ) + $comment->is_test = 'true'; + + $post = get_post( $comment->comment_post_ID ); + $comment->comment_post_modified_gmt = $post->post_modified_gmt; + + $response = self::http_post( build_query( $comment ), 'submit-ham' ); + if ( $comment->reporter ) { + self::update_comment_history( $comment_id, sprintf( __('%s reported this comment as not spam', 'akismet'), $comment->reporter ), 'report-ham' ); + update_comment_meta( $comment_id, 'akismet_user_result', 'false' ); + update_comment_meta( $comment_id, 'akismet_user', $comment->reporter ); + } + + do_action('akismet_submit_nonspam_comment', $comment_id, $response[1]); + } public static function cron_recheck() { global $wpdb; @@ -670,7 +822,14 @@ class Akismet { } public static function load_form_js() { - wp_enqueue_script( 'akismet-form', AKISMET__PLUGIN_URL . '_inc/form.js', array( 'jquery' ), AKISMET_VERSION ); + // WP < 3.3 can't enqueue a script this late in the game and still have it appear in the footer. + // Once we drop support for everything pre-3.3, this can change back to a single enqueue call. + wp_register_script( 'akismet-form', AKISMET__PLUGIN_URL . '_inc/form.js', array(), AKISMET_VERSION, true ); + add_action( 'wp_footer', array( 'Akismet', 'print_form_js' ) ); + add_action( 'admin_footer', array( 'Akismet', 'print_form_js' ) ); + } + + public static function print_form_js() { wp_print_scripts( 'akismet-form' ); } diff --git a/wp-content/plugins/akismet/readme.txt b/wp-content/plugins/akismet/readme.txt index 43604761..c9dce675 100644 --- a/wp-content/plugins/akismet/readme.txt +++ b/wp-content/plugins/akismet/readme.txt @@ -1,27 +1,26 @@ === Akismet === Contributors: matt, ryan, andy, mdawaffe, tellyworth, josephscott, lessbloat, eoigal, cfinke, automattic Tags: akismet, comments, spam -Requires at least: 3.0 -Tested up to: 3.9 -Stable tag: 3.0.0 +Requires at least: 3.1 +Tested up to: 3.9.1 +Stable tag: 3.0.1 License: GPLv2 or later -Akismet checks your comments against the Akismet web service to see if they look like spam or not. +Akismet checks your comments against the Akismet Web service to see if they look like spam or not. == Description == -Akismet checks your comments against the Akismet web service to see if they look like spam or not and lets you -review the spam it catches under your blog's "Comments" admin screen. +Akismet checks your comments against the Akismet Web service to see if they look like spam or not and lets you review the spam it catches under your blog's "Comments" admin screen. -Major new features in Akismet 2.5 include: +Major features in Akismet include: -* A comment status history, so you can easily see which comments were caught or cleared by Akismet, and which were spammed or unspammed by a moderator -* Links are highlighted in the comment body, to reveal hidden or misleading links -* If your web host is unable to reach Akismet's servers, the plugin will automatically retry when your connection is back up -* Moderators can see the number of approved comments for each user -* Spam and Unspam reports now include more information, to help improve accuracy +* Automatically checks all comments and filters out the ones that look like spam. +* Each comment has a status history, so you can easily see which comments were caught or cleared by Akismet and which were spammed or unspammed by a moderator. +* URLs are shown in the comment body to reveal hidden or misleading links. +* Moderators can see the number of approved comments for each user. +* A discard feature that outright blocks the worst spam, saving you disk space and speeding up your site. -PS: You'll need an [Akismet.com API key](http://akismet.com/get/) to use it. Keys are free for personal blogs, with paid subscriptions available for businesses and commercial sites. +PS: You'll need an [Akismet.com API key](http://akismet.com/get/) to use it. Keys are free for personal blogs; paid subscriptions are available for businesses and commercial sites. == Installation == @@ -31,6 +30,13 @@ Upload the Akismet plugin to your blog, Activate it, then enter your [Akismet.co == Changelog == += 3.0.1 = +* Removed dependency on PHP's fsockopen function +* Fix spam/ham reports to work when reported outside of the WP dashboard, e.g., from Notifications or the WP app +* Remove jQuery dependency for comment form JavaScript +* Remove unnecessary data from some Akismet comment meta +* Suspended keys will now result in all comments being put in moderation, not spam. + = 3.0.0 = * Move Akismet to Settings menu * Drop Akismet Stats menu @@ -38,7 +44,7 @@ Upload the Akismet plugin to your blog, Activate it, then enter your [Akismet.co * Add Akismet subscription details and status to Akismet settings * Add contextual help for each page * Improve Akismet setup to use Jetpack to automate plugin setup -* Fix Update Check for Spam to use ajax to avoid page timing out +* Fix "Check for Spam" to use AJAX to avoid page timing out * Fix Akismet settings page to be responsive * Drop legacy code * Tidy up CSS and Javascript diff --git a/wp-content/plugins/akismet/views/notice.php b/wp-content/plugins/akismet/views/notice.php index f613c373..0476929a 100644 --- a/wp-content/plugins/akismet/views/notice.php +++ b/wp-content/plugins/akismet/views/notice.php @@ -34,7 +34,7 @@

-

fsockopen or gethostbynamel functions. Akismet cannot work correctly until this is fixed. Please contact your web host or firewall administrator and give them this information about Akismet’s system requirements.', 'akismet'), 'https://blog.akismet.com/akismet-hosting-faq/'); ?>

+

gethostbynamel functions. Akismet cannot work correctly until this is fixed. Please contact your web host or firewall administrator and give them this information about Akismet’s system requirements.', 'akismet'), 'https://blog.akismet.com/akismet-hosting-faq/'); ?>

diff --git a/wp-content/plugins/akismet/wrapper.php b/wp-content/plugins/akismet/wrapper.php index 71d5abf8..12641c7e 100644 --- a/wp-content/plugins/akismet/wrapper.php +++ b/wp-content/plugins/akismet/wrapper.php @@ -15,7 +15,7 @@ function akismet_test_mode() { function akismet_http_post( $request, $host, $path, $port = 80, $ip = null ) { _deprecated_function( __FUNCTION__, '3.0', 'Akismet::http_post()' ); - $path = str_ireplace( '/1.1/', '', $path ); + $path = str_replace( '/1.1/', '', $path ); return Akismet::http_post( $request, $path, $ip ); } @@ -120,19 +120,19 @@ function akismet_check_for_spam_button( $comment_status ) { return Akismet_Admin::check_for_spam_button( $comment_status ); } function akismet_submit_nonspam_comment( $comment_id ) { - _deprecated_function( __FUNCTION__, '3.0', 'Akismet_Admin::submit_nonspam_comment()' ); + _deprecated_function( __FUNCTION__, '3.0', 'Akismet::submit_nonspam_comment()' ); - return Akismet_Admin::submit_nonspam_comment( $comment_id ); + return Akismet::submit_nonspam_comment( $comment_id ); } function akismet_submit_spam_comment( $comment_id ) { - _deprecated_function( __FUNCTION__, '3.0', 'Akismet_Admin::submit_spam_comment()' ); + _deprecated_function( __FUNCTION__, '3.0', 'Akismet::submit_spam_comment()' ); - return Akismet_Admin::submit_spam_comment( $comment_id ); + return Akismet::submit_spam_comment( $comment_id ); } function akismet_transition_comment_status( $new_status, $old_status, $comment ) { - _deprecated_function( __FUNCTION__, '3.0', 'Akismet_Admin::transition_comment_status()' ); + _deprecated_function( __FUNCTION__, '3.0', 'Akismet::transition_comment_status()' ); - return Akismet_Admin::transition_comment_status( $new_status, $old_status, $comment ); + return Akismet::transition_comment_status( $new_status, $old_status, $comment ); } function akismet_spam_count( $type = false ) { _deprecated_function( __FUNCTION__, '3.0', 'Akismet_Admin::get_spam_count()' ); @@ -290,4 +290,4 @@ function akismet_kill_proxy_check( $option ) { _deprecated_function( __FUNCTION__, '3.0' ); return 0; -} +} \ No newline at end of file diff --git a/wp-includes/ID3/getid3.lib.php b/wp-includes/ID3/getid3.lib.php index f8df2334..a7282c77 100644 --- a/wp-includes/ID3/getid3.lib.php +++ b/wp-includes/ID3/getid3.lib.php @@ -519,11 +519,12 @@ class getid3_lib } public static function XML2array($XMLstring) { - if (function_exists('simplexml_load_string')) { - if (function_exists('get_object_vars')) { - $XMLobject = simplexml_load_string($XMLstring); - return self::SimpleXMLelement2array($XMLobject); - } + if ( function_exists( 'simplexml_load_string' ) && function_exists( 'libxml_disable_entity_loader' ) ) { + $loader = libxml_disable_entity_loader( true ); + $XMLobject = simplexml_load_string( $XMLstring, 'SimpleXMLElement', LIBXML_NOENT ); + $return = self::SimpleXMLelement2array( $XMLobject ); + libxml_disable_entity_loader( $loader ); + return $return; } return false; } diff --git a/wp-includes/class-IXR.php b/wp-includes/class-IXR.php index 7264db6e..f576719c 100644 --- a/wp-includes/class-IXR.php +++ b/wp-includes/class-IXR.php @@ -203,11 +203,37 @@ class IXR_Message { // first remove the XML declaration // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages - $header = preg_replace( '/<\?xml.*?\?'.'>/', '', substr($this->message, 0, 100), 1); - $this->message = substr_replace($this->message, $header, 0, 100); - if (trim($this->message) == '') { + $header = preg_replace( '/<\?xml.*?\?'.'>/s', '', substr( $this->message, 0, 100 ), 1 ); + $this->message = trim( substr_replace( $this->message, $header, 0, 100 ) ); + if ( '' == $this->message ) { return false; } + + // Then remove the DOCTYPE + $header = preg_replace( '/^]*+>/i', '', substr( $this->message, 0, 200 ), 1 ); + $this->message = trim( substr_replace( $this->message, $header, 0, 200 ) ); + if ( '' == $this->message ) { + return false; + } + + // Check that the root tag is valid + $root_tag = substr( $this->message, 0, strcspn( substr( $this->message, 0, 20 ), "> \t\r\n" ) ); + if ( 'message, '<' ) ) { + return false; + } + $this->_parser = xml_parser_create(); // Set XML parser to take the case of tags in to account xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); diff --git a/wp-includes/class-wp-customize-widgets.php b/wp-includes/class-wp-customize-widgets.php index 8efead24..40663f29 100644 --- a/wp-includes/class-wp-customize-widgets.php +++ b/wp-includes/class-wp-customize-widgets.php @@ -1119,22 +1119,19 @@ final class WP_Customize_Widgets { } /** - * Get a widget instance's hash key. + * Get MAC for a serialized widget instance string. * - * Serialize an instance and hash it with the AUTH_KEY; when a JS value is - * posted back to save, this instance hash key is used to ensure that the - * serialized_instance was not tampered with, but that it had originated - * from WordPress and so is sanitized. + * Allows values posted back from JS to be rejected if any tampering of the + * data has occurred. * * @since 3.9.0 * @access protected * - * @param array $instance Widget instance. - * @return string Widget instance's hash key. + * @param string $serialized_instance Widget instance. + * @return string MAC for serialized widget instance. */ - protected function get_instance_hash_key( $instance ) { - $hash = md5( AUTH_KEY . serialize( $instance ) ); - return $hash; + protected function get_instance_hash_key( $serialized_instance ) { + return wp_hash( $serialized_instance ); } /** @@ -1162,18 +1159,19 @@ final class WP_Customize_Widgets { } $decoded = base64_decode( $value['encoded_serialized_instance'], true ); - if ( false === $decoded ) { return null; } - $instance = unserialize( $decoded ); - if ( false === $instance ) { + if ( $this->get_instance_hash_key( $decoded ) !== $value['instance_hash_key'] ) { return null; } - if ( $this->get_instance_hash_key( $instance ) !== $value['instance_hash_key'] ) { + + $instance = unserialize( $decoded ); + if ( false === $instance ) { return null; } + return $instance; } @@ -1194,7 +1192,7 @@ final class WP_Customize_Widgets { 'encoded_serialized_instance' => base64_encode( $serialized ), 'title' => empty( $value['title'] ) ? '' : $value['title'], 'is_widget_customizer_js_value' => true, - 'instance_hash_key' => $this->get_instance_hash_key( $value ), + 'instance_hash_key' => $this->get_instance_hash_key( $serialized ), ); } return $value; diff --git a/wp-includes/compat.php b/wp-includes/compat.php index cb2a5597..83a8c646 100644 --- a/wp-includes/compat.php +++ b/wp-includes/compat.php @@ -94,3 +94,32 @@ if ( !function_exists('json_decode') ) { return is_array($data) ? array_map(__FUNCTION__, $data) : $data; } } + +if ( ! function_exists( 'hash_equals' ) ) : +/** + * Compare two strings in constant time. + * + * This function was added in PHP 5.6. + * It can leak the length of a string. + * + * @since 3.9.2 + * + * @param string $a Expected string. + * @param string $b Actual string. + * @return bool Whether strings are equal. + */ +function hash_equals( $a, $b ) { + $a_length = strlen( $a ); + if ( $a_length !== strlen( $b ) ) { + return false; + } + $result = 0; + + // Do not attempt to "optimize" this. + for ( $i = 0; $i < $a_length; $i++ ) { + $result |= ord( $a[ $i ] ) ^ ord( $b[ $i ] ); + } + + return $result === 0; +} +endif; \ No newline at end of file diff --git a/wp-includes/pluggable.php b/wp-includes/pluggable.php index ef2821fb..dae13a4a 100644 --- a/wp-includes/pluggable.php +++ b/wp-includes/pluggable.php @@ -647,7 +647,7 @@ function wp_validate_auth_cookie($cookie = '', $scheme = '') { $key = wp_hash($username . $pass_frag . '|' . $expiration, $scheme); $hash = hash_hmac('md5', $username . '|' . $expiration, $key); - if ( hash_hmac( 'md5', $hmac, $key ) !== hash_hmac( 'md5', $hash, $key ) ) { + if ( ! hash_equals( $hash, $hmac ) ) { /** * Fires if a bad authentication cookie hash is encountered. * @@ -1658,11 +1658,17 @@ function wp_verify_nonce($nonce, $action = -1) { $i = wp_nonce_tick(); // Nonce generated 0-12 hours ago - if ( substr(wp_hash($i . $action . $uid, 'nonce'), -12, 10) === $nonce ) + $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid, 'nonce'), -12, 10 ); + if ( hash_equals( $expected, $nonce ) ) { return 1; + } + // Nonce generated 12-24 hours ago - if ( substr(wp_hash(($i - 1) . $action . $uid, 'nonce'), -12, 10) === $nonce ) + $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid, 'nonce' ), -12, 10 ); + if ( hash_equals( $expected, $nonce ) ) { return 2; + } + // Invalid nonce return false; } @@ -1687,7 +1693,7 @@ function wp_create_nonce($action = -1) { $i = wp_nonce_tick(); - return substr(wp_hash($i . $action . $uid, 'nonce'), -12, 10); + return substr(wp_hash($i . '|' . $action . '|' . $uid, 'nonce'), -12, 10); } endif; @@ -2107,7 +2113,8 @@ function get_avatar( $id_or_email, $size = '96', $default = '', $alt = false ) { $out = str_replace( '&', '&', esc_url( $out ) ); $avatar = "{$safe_alt}"; } else { - $avatar = "{$safe_alt}"; + $out = esc_url( $default ); + $avatar = "{$safe_alt}"; } /** @@ -2200,3 +2207,35 @@ function wp_text_diff( $left_string, $right_string, $args = null ) { } endif; +if ( ! function_exists( 'hash_equals' ) ) : +/** + * Compare two strings in constant time. + * + * This function is NOT pluggable. It is in this file (in addition to + * compat.php) to prevent errors if, during an update, pluggable.php + * copies over but compat.php does not. + * + * This function was added in PHP 5.6. + * It can leak the length of a string. + * + * @since 3.9.2 + * + * @param string $a Expected string. + * @param string $b Actual string. + * @return bool Whether strings are equal. + */ +function hash_equals( $a, $b ) { + $a_length = strlen( $a ); + if ( $a_length !== strlen( $b ) ) { + return false; + } + $result = 0; + + // Do not attempt to "optimize" this. + for ( $i = 0; $i < $a_length; $i++ ) { + $result |= ord( $a[ $i ] ) ^ ord( $b[ $i ] ); + } + + return $result === 0; +} +endif; diff --git a/wp-includes/version.php b/wp-includes/version.php index 1de7b45f..17a1b0ad 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -4,7 +4,7 @@ * * @global string $wp_version */ -$wp_version = '3.9.1'; +$wp_version = '3.9.2'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. diff --git a/wp-login.php b/wp-login.php index 7bb53343..0197167f 100644 --- a/wp-login.php +++ b/wp-login.php @@ -562,10 +562,25 @@ break; case 'resetpass' : case 'rp' : - $user = check_password_reset_key($_GET['key'], $_GET['login']); + list( $rp_path ) = explode( '?', wp_unslash( $_SERVER['REQUEST_URI'] ) ); + $rp_cookie = 'wp-resetpass-' . COOKIEHASH; + if ( isset( $_GET['key'] ) ) { + $value = sprintf( '%s:%s', wp_unslash( $_GET['login'] ), wp_unslash( $_GET['key'] ) ); + setcookie( $rp_cookie, $value, 0, $rp_path, COOKIE_DOMAIN, is_ssl(), true ); + wp_safe_redirect( remove_query_arg( array( 'key', 'login' ) ) ); + exit; + } + + if ( isset( $_COOKIE[ $rp_cookie ] ) && 0 < strpos( $_COOKIE[ $rp_cookie ], ':' ) ) { + list( $rp_login, $rp_key ) = explode( ':', wp_unslash( $_COOKIE[ $rp_cookie ] ), 2 ); + $user = check_password_reset_key( $rp_key, $rp_login ); + } else { + $user = false; + } - if ( is_wp_error($user) ) { - if ( $user->get_error_code() === 'expired_key' ) + if ( ! $user || is_wp_error( $user ) ) { + setcookie( $rp_cookie, ' ', time() - YEAR_IN_SECONDS, $rp_path, COOKIE_DOMAIN, is_ssl(), true ); + if ( $user && $user->get_error_code() === 'expired_key' ) wp_redirect( site_url( 'wp-login.php?action=lostpassword&error=expiredkey' ) ); else wp_redirect( site_url( 'wp-login.php?action=lostpassword&error=invalidkey' ) ); @@ -589,6 +604,7 @@ case 'rp' : if ( ( ! $errors->get_error_code() ) && isset( $_POST['pass1'] ) && !empty( $_POST['pass1'] ) ) { reset_password($user, $_POST['pass1']); + setcookie( $rp_cookie, ' ', time() - YEAR_IN_SECONDS, $rp_path, COOKIE_DOMAIN, is_ssl(), true ); login_header( __( 'Password Reset' ), '

' . __( 'Your password has been reset.' ) . ' ' . __( 'Log in' ) . '

' ); login_footer(); exit; @@ -600,8 +616,8 @@ case 'rp' : login_header(__('Reset Password'), '

' . __('Enter your new password below.') . '

', $errors ); ?> -
- + +