WordPress 3.9.2 wordpress-3.9.2
authorEdward Z. Yang <ezyang@cs.stanford.edu>
Sun, 10 Aug 2014 01:27:54 +0000 (02:27 +0100)
committerEdward Z. Yang <ezyang@cs.stanford.edu>
Sun, 10 Aug 2014 01:27:54 +0000 (02:27 +0100)
Signed-off-by: Edward Z. Yang <ezyang@cs.stanford.edu>
19 files changed:
readme.html
wp-admin/about.php
wp-content/plugins/akismet/.htaccess [new file with mode: 0644]
wp-content/plugins/akismet/_inc/akismet.css
wp-content/plugins/akismet/_inc/akismet.js
wp-content/plugins/akismet/_inc/form.js
wp-content/plugins/akismet/akismet.php
wp-content/plugins/akismet/class.akismet-admin.php
wp-content/plugins/akismet/class.akismet.php
wp-content/plugins/akismet/readme.txt
wp-content/plugins/akismet/views/notice.php
wp-content/plugins/akismet/wrapper.php
wp-includes/ID3/getid3.lib.php
wp-includes/class-IXR.php
wp-includes/class-wp-customize-widgets.php
wp-includes/compat.php
wp-includes/pluggable.php
wp-includes/version.php
wp-login.php

index faad3e9b6c3537f761cd0dd5d3055c888b2c4f29..914fb2a4a07736e6a3db874f0f962f9c185a4eaa 100644 (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 3.9.1
+       <br /> Version 3.9.2
 </h1>
 <p style="text-align: center">Semantic Personal Publishing Platform</p>
 
index 0a4344b135b5c48148eceabb5fa2d40b211ce8e6..f5c786012f55904ee476352247f917957e251c72 100644 (file)
@@ -39,7 +39,11 @@ include( ABSPATH . 'wp-admin/admin-header.php' );
 </h2>
 
 <div class="changelog point-releases">
-       <h3><?php echo _n( 'Maintenance Release', 'Maintenance Releases', 1 ); ?></h3>
+       <h3><?php echo _n( 'Maintenance and Security Release', 'Maintenance and Security Releases', 2 ); ?></h3>
+       <p><?php printf( _n( '<strong>Version %1$s</strong> addressed a security issue.',
+         '<strong>Version %1$s</strong> addressed some security issues.', 6 ), '3.9.2', number_format_i18n( 6 ) ); ?>
+               <?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), 'http://codex.wordpress.org/Version_3.9.2' ); ?>
+       </p>
        <p><?php printf( _n( '<strong>Version %1$s</strong> addressed %2$s bug.',
          '<strong>Version %1$s</strong> addressed %2$s bugs.', 34 ), '3.9.1', number_format_i18n( 34 ) ); ?>
                <?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), '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 (file)
index 0000000..6d8b4ec
--- /dev/null
@@ -0,0 +1,11 @@
+Order Deny,Allow
+Deny from all
+
+<FilesMatch "^(form|akismet)\.(css|js)$">
+       Allow from all
+</FilesMatch>
+
+#allow access to any image
+<FilesMatch "^(.+)\.(png|gif)$">
+       Allow from all
+</FilesMatch>
\ No newline at end of file
index e1d6fd533b1e99586c3bdfa6ece017ee4997da11..ff076aab2ba1fde44618e6e9a47a9bb83c27063a 100644 (file)
@@ -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 {
index ed65220cf8b868253442ae096b1f876074706901..aa0aab42866530623d0dee0a9b145d14671782c6 100644 (file)
@@ -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();
        });
index ec5e56e7187e41291a54fc97dd531a7808ade7d4..3a5be8af02203eef6449c0b2c37ff914aaa6a4dd 100644 (file)
@@ -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 = $( '<input type="hidden" id="ak_js" name="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
index 811ab717b8afeb18a2f7963aa7b7e7b66a16c0aa..7fc1e9db56212bee69501ce3b070c8c17a906c9f 100644 (file)
@@ -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 <strong>protect your blog from comment and trackback spam</strong>. 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) <a href="http://akismet.com/get/">Sign up for an Akismet API key</a>, 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 );
index 15f5d479718d6cfa5ab38ad1c629b738e7947ef4..7a62ccbd6453cba7350954deb1cee08b20cf6c79 100644 (file)
@@ -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 = '<a href="'.self::get_page_url().'">'.__('Settings', 'akismet').'</a>';
+               $settings_link = '<a href="'.esc_url( self::get_page_url() ).'">'.__('Settings', 'akismet').'</a>';
                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 '</div><div class="alignleft"><a class="button-secondary checkforspam" href="' . esc_url( $link ) . '">' . esc_html__('Check for Spam', 'akismet') . '</a>';
-               echo '<img src="' . esc_url( admin_url( 'images/wpspin_light.gif' ) ) . '" class="checkforspam-spinner" />';
-       }
-
-       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 '</div><div class="alignleft"><a class="button-secondary checkforspam" href="' . esc_url( $link ) . '">' . esc_html__('Check for Spam', 'akismet') . '</a><span class="checkforspam-spinner"></span>';
        }
 
        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 '<span class="akismet-status" commentid="'.$comment->comment_ID.'"><a href="comment.php?action=editcomment&amp;c='.$comment->comment_ID.'#akismet-status" title="' . esc_attr__( 'View comment history' , 'akismet') . '">'.esc_html( $desc ).'</a></span>';
 
-               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 '<span class="akismet-user-comment-count" commentid="'.$comment->comment_ID.'" style="display:none;"><br><span class="akismet-user-comment-counts">'. sprintf( esc_html( _n( '%s approved', '%s approved', $comment_count , 'akismet') ), number_format_i18n( $comment_count ) ) . '</span></span>';
@@ -557,102 +517,6 @@ class Akismet_Admin {
                return preg_replace_callback( '#<a ([^>]*)href="([^"]+)"([^>]*)>(.*?)</a>#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 <a href="%s">Akismet configuration</a> 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
index 7cf813d8212808a4a85fed491e8208b7a922c920..17f6b0058acd846465abb38aa3091f2f62536124 100644 (file)
@@ -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' );
        }
 
index 43604761dcff62231e139feab517586555fbcd4f..c9dce67585d13af389e26d687621ab8439e8281f 100644 (file)
@@ -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
index f613c373da48cb5ce74694832774e382fb025bae..0476929ab71f918ce7d8e8de40488fa04b50078c 100644 (file)
@@ -34,7 +34,7 @@
 <?php elseif ( $type == 'missing-functions' ) :?>
 <div class="wrap alert critical">
        <h3 class="key-status failed"><?php esc_html_e('Network functions are disabled.', 'akismet'); ?></h3>
-       <p class="description"><?php printf( __('Your web host or server administrator has disabled PHP&#8217;s <code>fsockopen</code> or <code>gethostbynamel</code> functions.  <strong>Akismet cannot work correctly until this is fixed.</strong>  Please contact your web host or firewall administrator and give them <a href="%s" target="_blank">this information about Akismet&#8217;s system requirements</a>.', 'akismet'), 'https://blog.akismet.com/akismet-hosting-faq/'); ?></p>
+       <p class="description"><?php printf( __('Your web host or server administrator has disabled PHP&#8217;s <code>gethostbynamel</code> functions.  <strong>Akismet cannot work correctly until this is fixed.</strong>  Please contact your web host or firewall administrator and give them <a href="%s" target="_blank">this information about Akismet&#8217;s system requirements</a>.', 'akismet'), 'https://blog.akismet.com/akismet-hosting-faq/'); ?></p>
 </div>
 <?php elseif ( $type == 'servers-be-down' ) :?>
 <div class="wrap alert critical">
index 71d5abf818e9e623d4a6dbb7c8ab25c88c8e29ff..12641c7ec7559595f5a26de2fc78e5c83a2ee530 100644 (file)
@@ -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
index f8df2334865b1857115a383bbb653855164ed8fa..a7282c77c0652634ca52c123b14a054e018a9cab 100644 (file)
@@ -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;
        }
index 7264db6efb45f06f7ff6a92466c47d676a770590..f576719c87c52dcaa8cd80483f6ed22ea8d12603 100644 (file)
@@ -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( '/^<!DOCTYPE[^>]*+>/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 ( '<!DOCTYPE' === strtoupper( $root_tag ) ) {
+            return false;
+        }
+        if ( ! in_array( $root_tag, array( '<methodCall', '<methodResponse', '<fault' ) ) ) {
+            return false;
+        }
+
+        // Bail if there are too many elements to parse
+        $element_limit = 30000;
+        if ( function_exists( 'apply_filters' ) ) {
+            $element_limit = apply_filters( 'xmlrpc_element_limit', $element_limit );
+        }
+        if ( $element_limit && 2 * $element_limit < substr_count( $this->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);
index 8efead24786167189f2654eee7707b1f384ad8aa..40663f29379c575ae1d489098bb1a7045d66ca49 100644 (file)
@@ -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;
index cb2a5597c8cde15867682100cfbf507a2016bc6d..83a8c64652b4679b720df879e02ac903e75e7d42 100644 (file)
@@ -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
index ef2821fb2ad46d69340a0117a360eb696c716989..dae13a4ae0faa06f0e2187315762fdd91555362c 100644 (file)
@@ -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( '&#038;', '&amp;', esc_url( $out ) );
                $avatar = "<img alt='{$safe_alt}' src='{$out}' class='avatar avatar-{$size} photo' height='{$size}' width='{$size}' />";
        } else {
-               $avatar = "<img alt='{$safe_alt}' src='{$default}' class='avatar avatar-{$size} photo avatar-default' height='{$size}' width='{$size}' />";
+               $out = esc_url( $default );
+               $avatar = "<img alt='{$safe_alt}' src='{$out}' class='avatar avatar-{$size} photo avatar-default' height='{$size}' width='{$size}' />";
        }
 
        /**
@@ -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;
index 1de7b45fb618edbe3fe0edfe94a083948b691f33..17a1b0ad55b3009ef70b00ac305709a1e9af7f10 100644 (file)
@@ -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.
index 7bb53343d16e14fdaf4a4e39b21d2faa28eed312..0197167f65cb1c00d6ded3ec669b017177b59917 100644 (file)
@@ -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' ), '<p class="message reset-pass">' . __( 'Your password has been reset.' ) . ' <a href="' . esc_url( wp_login_url() ) . '">' . __( 'Log in' ) . '</a></p>' );
                login_footer();
                exit;
@@ -600,8 +616,8 @@ case 'rp' :
        login_header(__('Reset Password'), '<p class="message reset-pass">' . __('Enter your new password below.') . '</p>', $errors );
 
 ?>
-<form name="resetpassform" id="resetpassform" action="<?php echo esc_url( site_url( 'wp-login.php?action=resetpass&key=' . urlencode( $_GET['key'] ) . '&login=' . urlencode( $_GET['login'] ), 'login_post' ) ); ?>" method="post" autocomplete="off">
-       <input type="hidden" id="user_login" value="<?php echo esc_attr( $_GET['login'] ); ?>" autocomplete="off" />
+<form name="resetpassform" id="resetpassform" action="<?php echo esc_url( site_url( 'wp-login.php?action=resetpass', 'login_post' ) ); ?>" method="post" autocomplete="off">
+       <input type="hidden" id="user_login" value="<?php echo esc_attr( $rp_login ); ?>" autocomplete="off" />
 
        <p>
                <label for="pass1"><?php _e('New password') ?><br />