diff -r c7c0fbc09788 -r 5e8dcbe22c24 web/wp-content/plugins/bbpress/includes/extend/akismet.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/wp-content/plugins/bbpress/includes/extend/akismet.php Tue Dec 04 18:43:10 2012 -0800 @@ -0,0 +1,686 @@ +setup_actions(); + } + + /** + * Setup the admin hooks + * + * @since bbPress (r3277) + * @access private + * + * @uses add_filter() To add various filters + * @uses add_action() To add various actions + */ + private function setup_actions() { + + // Prevent debug notices + $checks = array(); + + // bbPress functions to check for spam + $checks['check'] = array( + 'bbp_new_topic_pre_insert' => 1, // New topic check + 'bbp_new_reply_pre_insert' => 1, // New reply check + 'bbp_edit_topic_pre_insert' => 1, // Edit topic check + 'bbp_edit_reply_pre_insert' => 1 // Edit reply check + ); + + // bbPress functions for spam and ham submissions + $checks['submit'] = array( + 'bbp_spammed_topic' => 10, // Spammed topic + 'bbp_unspammed_topic' => 10, // Unspammed reply + 'bbp_spammed_reply' => 10, // Spammed reply + 'bbp_unspammed_reply' => 10, // Unspammed reply + ); + + // Add the checks + foreach ( $checks as $type => $functions ) + foreach( $functions as $function => $priority ) + add_filter( $function, array( $this, $type . '_post' ), $priority ); + + // Update post meta + add_action( 'wp_insert_post', array( $this, 'update_post_meta' ), 10, 2 ); + } + + /** + * Converts topic/reply data into Akismet comment checking format + * + * @since bbPress (r3277) + * + * @param string $post_data + * + * @uses get_userdata() To get the user data + * @uses bbp_filter_anonymous_user_data() To get anonymous user data + * @uses bbp_get_topic_permalink() To get the permalink of the topic + * @uses bbp_get_reply_url() To get the permalink of the reply + * @uses bbp_current_author_ip() To get the IP address of the current user + * @uses BBP_Akismet::maybe_spam() To check if post is spam + * @uses akismet_get_user_roles() To get the role(s) of the current user + * @uses do_action() To call the 'bbp_akismet_spam_caught' hook + * @uses add_filter() To call the 'bbp_new_reply_pre_set_terms' hook + * + * @return array Array of post data + */ + public function check_post( $post_data ) { + + // Define local variables + $user_data = array(); + $post_permalink = ''; + + // Post is not published + if ( bbp_get_public_status_id() != $post_data['post_status'] ) + return $post_data; + + // Cast the post_author to 0 if it's empty + if ( empty( $post_data['post_author'] ) ) + $post_data['post_author'] = 0; + + /** Author ************************************************************/ + + // Get user data + $userdata = get_userdata( $post_data['post_author'] ); + $anonymous_data = bbp_filter_anonymous_post_data(); + + // Author is anonymous + if ( !empty( $anonymous_data ) ) { + $user_data['name'] = $anonymous_data['bbp_anonymous_name']; + $user_data['email'] = $anonymous_data['bbp_anonymous_name']; + $user_data['website'] = $anonymous_data['bbp_anonymous_name']; + + // Author is logged in + } elseif ( !empty( $userdata ) ) { + $user_data['name'] = $userdata->display_name; + $user_data['email'] = $userdata->user_email; + $user_data['website'] = $userdata->user_url; + + // Missing author data, so set some empty strings + } else { + $user_data['name'] = ''; + $user_data['email'] = ''; + $user_data['website'] = ''; + } + + /** Post **************************************************************/ + + // Use post parent for permalink + if ( !empty( $post_data['post_parent'] ) ) + $post_permalink = get_permalink( $post_data['post_parent'] ); + + // Put post_data back into usable array + $_post = array( + 'comment_author' => $user_data['name'], + 'comment_author_email' => $user_data['email'], + 'comment_author_url' => $user_data['website'], + 'comment_content' => $post_data['post_content'], + 'comment_post_ID' => $post_data['post_parent'], + 'comment_type' => $post_data['post_type'], + 'permalink' => $post_permalink, + 'referrer' => $_SERVER['HTTP_REFERER'], + 'user_agent' => $_SERVER['HTTP_USER_AGENT'], + 'user_ID' => $post_data['post_author'], + 'user_ip' => bbp_current_author_ip(), + 'user_role' => akismet_get_user_roles( $post_data['post_author'] ), + ); + + // Check the post_data + $_post = $this->maybe_spam( $_post ); + + // Get the result + $post_data['bbp_akismet_result'] = $_post['bbp_akismet_result']; + unset( $_post['bbp_akismet_result'] ); + + // Store the data as submitted + $post_data['bbp_post_as_submitted'] = $_post; + + // Allow post_data to be manipulated + do_action_ref_array( 'bbp_akismet_check_post', $post_data ); + + // Spam + if ( 'true' == $post_data['bbp_akismet_result'] ) { + + // Let plugins do their thing + do_action( 'bbp_akismet_spam_caught' ); + + // This is spam + $post_data['post_status'] = bbp_get_spam_status_id(); + + // We don't want your spam tags here + add_filter( 'bbp_new_reply_pre_set_terms', array( $this, 'filter_post_terms' ), 1, 3 ); + + // @todo Spam counter? + } + + // @todo Topic/reply moderation? No true/false response - 'pending' or 'draft' + // @todo Auto-delete old spam? + + // Log the last post + $this->last_post = $post_data; + + // Pass the data back to the filter + return $post_data; + } + + /** + * Submit a post for spamming or hamming + * + * @since bbPress (r3277) + * + * @param int $post_id + * + * @global WP_Query $wpdb + * @global string $akismet_api_host + * @global string $akismet_api_port + * @global object $current_user + * @global object $current_site + * + * @uses current_filter() To get the reply_id + * @uses get_post() To get the post object + * @uses get_the_author_meta() To get the author meta + * @uses get_post_meta() To get the post meta + * @uses bbp_get_user_profile_url() To get a user's profile url + * @uses get_permalink() To get the permalink of the post_parent + * @uses akismet_get_user_roles() To get the role(s) of the post_author + * @uses bbp_current_author_ip() To get the IP address of the current user + * @uses BBP_Akismet::maybe_spam() To submit the post as ham or spam + * @uses update_post_meta() To update the post meta with some Akismet data + * @uses do_action() To call the 'bbp_akismet_submit_spam_post' and 'bbp_akismet_submit_ham_post' hooks + * + * @return array Array of existing topic terms + */ + public function submit_post( $post_id = 0 ) { + global $current_user, $current_site; + + // Innocent until proven guilty + $request_type = 'ham'; + $current_filter = current_filter(); + + // Check this filter and adjust the $request_type accordingly + switch ( $current_filter ) { + + // Mysterious, and straight from the can + case 'bbp_spammed_topic' : + case 'bbp_spammed_reply' : + $request_type = 'spam'; + break; + + // Honey-glazed, a straight off the bone + case 'bbp_unspammed_topic' : + case 'bbp_unspammed_reply' : + $request_type = 'ham'; + break; + + // Possibly poison... + default : + return; + } + + // Setup some variables + $post_id = (int) $post_id; + + // Make sure we have a post + $_post = get_post( $post_id ); + + // Bail if get_post() fails + if ( empty( $_post ) ) + return; + + // Bail if we're spamming, but the post_status isn't spam + if ( ( 'spam' == $request_type ) && ( bbp_get_spam_status_id() != $_post->post_status ) ) + return; + + // Set some default post_data + $post_data = array( + 'comment_approved' => $_post->post_status, + 'comment_author' => $_post->post_author ? get_the_author_meta( 'display_name', $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_name', true ), + 'comment_author_email' => $_post->post_author ? get_the_author_meta( 'email', $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_email', true ), + 'comment_author_url' => $_post->post_author ? bbp_get_user_profile_url( $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_website', true ), + 'comment_content' => $_post->post_content, + 'comment_date' => $_post->post_date, + 'comment_ID' => $post_id, + 'comment_post_ID' => $_post->post_parent, + 'comment_type' => $_post->post_type, + 'permalink' => get_permalink( $post_id ), + 'user_ID' => $_post->post_author, + 'user_ip' => get_post_meta( $post_id, '_bbp_author_ip', true ), + 'user_role' => akismet_get_user_roles( $_post->post_author ), + ); + + // Use the original version stored in post_meta if available + $as_submitted = get_post_meta( $post_id, '_bbp_akismet_as_submitted', true ); + if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) + $post_data = array_merge( $post_data, $as_submitted ); + + // Add the reporter IP address + $post_data['reporter_ip'] = bbp_current_author_ip(); + + // Add some reporter info + if ( is_object( $current_user ) ) + $post_data['reporter'] = $current_user->user_login; + + // Add the current site domain + if ( is_object( $current_site ) ) + $post_data['site_domain'] = $current_site->domain; + + // Place your slide beneath the microscope + $post_data = $this->maybe_spam( $post_data, 'submit', $request_type ); + + // Manual user action + if ( isset( $post_data['reporter'] ) ) { + + // What kind of action + switch ( $request_type ) { + + // Spammy + case 'spam' : + $this->update_post_history( $post_id, sprintf( __( '%1$s reported this %2$s as spam', 'bbpress' ), $post_data['reporter'], $post_data['comment_type'] ), 'report-spam' ); + update_post_meta( $post_id, '_bbp_akismet_user_result', 'true' ); + update_post_meta( $post_id, '_bbp_akismet_user', $post_data['reporter'] ); + break; + + // Hammy + case 'ham' : + $this->update_post_history( $post_id, sprintf( __( '%1$s reported this %2$s as not spam', 'bbpress' ), $post_data['reporter'], $post_data['comment_type'] ), 'report-ham' ); + update_post_meta( $post_id, '_bbp_akismet_user_result', 'false' ); + update_post_meta( $post_id, '_bbp_akismet_user', $post_data['reporter'] ); + + // @todo Topic term revision history + break; + + // Possible other actions + default : + break; + } + } + + do_action( 'bbp_akismet_submit_' . $request_type . '_post', $post_id, $post_data['bbp_akismet_result'] ); + } + + /** + * Ping Akismet service and check for spam/ham response + * + * @since bbPress (r3277) + * + * @param array $post_data + * @param string $check Accepts check|submit + * @param string $spam Accepts spam|ham + * + * @global string $akismet_api_host + * @global string $akismet_api_port + * + * @uses akismet_test_mode() To determine if Akismet is in test mode + * @uses akismet_http_post() To send data to the mothership for processing + * + * @return array Array of post data + */ + private function maybe_spam( $post_data, $check = 'check', $spam = 'spam' ) { + global $akismet_api_host, $akismet_api_port; + + // Define variables + $query_string = $path = $response = ''; + + // Populate post data + $post_data['blog'] = get_option( 'home' ); + $post_data['blog_charset'] = get_option( 'blog_charset' ); + $post_data['blog_lang'] = get_locale(); + $post_data['referrer'] = $_SERVER['HTTP_REFERER']; + $post_data['user_agent'] = $_SERVER['HTTP_USER_AGENT']; + + // Akismet Test Mode + if ( akismet_test_mode() ) + $post_data['is_test'] = 'true'; + + // Loop through _POST args and rekey strings + foreach ( $_POST as $key => $value ) + if ( is_string( $value ) ) + $post_data['POST_' . $key] = $value; + + // Keys to ignore + $ignore = array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' ); + + // Loop through _SERVER args and remove whitelisted keys + foreach ( $_SERVER as $key => $value ) { + + // Key should not be ignored + if ( !in_array( $key, $ignore ) && is_string( $value ) ) { + $post_data[$key] = $value; + + // Key should be ignored + } else { + $post_data[$key] = ''; + } + } + + // Ready... + foreach ( $post_data as $key => $data ) + $query_string .= $key . '=' . urlencode( stripslashes( $data ) ) . '&'; + + // Aim... + if ( 'check' == $check ) { + $path = '/1.1/comment-check'; + } elseif ( 'submit' == $check ) { + $path = '/1.1/submit-' . $spam; + } + + // Fire! + $response = $this->http_post( $query_string, $akismet_api_host, $path, $akismet_api_port ); + + // Check the high-speed cam + if ( !empty( $response[1] ) ) { + $post_data['bbp_akismet_result'] = $response[1]; + } else { + $post_data['bbp_akismet_result'] = __( 'No response', 'bbpress' ); + } + + // This is ham + return $post_data; + } + + /** + * Update post meta after a spam check + * + * @since bbPress (r3308) + * + * @param int $post_id + * @param object $_post + * + * @global object $this->last_post + * + * @uses get_post() To get the post object + * @uses get_userdata() To get the user data + * @uses bbp_filter_anonymous_user_data() To get anonymous user data + * @uses update_post_meta() To update post meta with Akismet data + * @uses BBP_Akismet::update_post_history() To update post Akismet history + */ + public function update_post_meta( $post_id = 0, $_post = false ) { + + // Define local variable(s) + $as_submitted = false; + + // Setup some variables + $post_id = (int) $post_id; + + // Ensure we have a post object + if ( empty( $_post ) ) + $_post = get_post( $post_id ); + + // Set up Akismet last post data + if ( !empty( $this->last_post ) ) + $as_submitted = $this->last_post['bbp_post_as_submitted']; + + // wp_insert_post() might be called in other contexts. Ensure this is + // the same topic/reply as was checked by BBP_Akismet::check_post() + if ( is_object( $_post ) && !empty( $this->last_post ) && is_array( $as_submitted ) ) { + + // Get user data + $userdata = get_userdata( $_post->post_author ); + $anonymous_data = bbp_filter_anonymous_post_data(); + + // More checks + if ( intval( $as_submitted['comment_post_ID'] ) == intval( $_post->post_parent ) + && $as_submitted['comment_author'] == ( $anonymous_data ? $anonymous_data['bbp_anonymous_name'] : $userdata->display_name ) + && $as_submitted['comment_author_email'] == ( $anonymous_data ? $anonymous_data['bbp_anonymous_email'] : $userdata->user_email ) + ) { + + // Normal result: true + if ( $this->last_post['bbp_akismet_result'] == 'true' ) { + + // Leave a trail so other's know what we did + update_post_meta( $post_id, '_bbp_akismet_result', 'true' ); + $this->update_post_history( $post_id, __( 'Akismet caught this post as spam', 'bbpress' ), 'check-spam' ); + + // If post_status isn't the spam status, as expected, leave a note + if ( $_post->post_status != bbp_get_spam_status_id() ) { + $this->update_post_history( $post_id, sprintf( __( 'Post status was changed to %s', 'bbpress' ), $_post->post_status ), 'status-changed-' . $_post->post_status ); + } + + // Normal result: false + } elseif ( $this->last_post['bbp_akismet_result'] == 'false' ) { + + // Leave a trail so other's know what we did + update_post_meta( $post_id, '_bbp_akismet_result', 'false' ); + $this->update_post_history( $post_id, __( 'Akismet cleared this post', 'bbpress' ), 'check-ham' ); + + // If post_status is the spam status, which isn't expected, leave a note + if ( $_post->post_status == bbp_get_spam_status_id() ) { + + // @todo Use wp_blacklist_check() + + $this->update_post_history( $post_id, sprintf( __( 'Post status was changed to %s', 'bbpress' ), $_post->post_status ), 'status-changed-' . $_post->post_status ); + } + + // Abnormal result: error + } else { + // Leave a trail so other's know what we did + update_post_meta( $post_id, '_bbp_akismet_error', time() ); + $this->update_post_history( $post_id, sprintf( __( 'Akismet was unable to check this post (response: %s), will automatically retry again later.', 'bbpress' ), $this->last_post['bbp_akismet_result'] ), 'check-error' ); + } + + // Record the complete original data as submitted for checking + if ( isset( $this->last_post['bbp_post_as_submitted'] ) ) { + update_post_meta( $post_id, '_bbp_akismet_as_submitted', $this->last_post['bbp_post_as_submitted'] ); + } + } + } + } + + /** + * Update a post's Akismet history + * + * @since bbPress (r3308) + * + * @param int $post_id + * @param string $message + * @param string $event + * + * @uses wp_get_current_user() To get the current_user object + * @uses add_post_meta() Add Akismet post history + */ + private function update_post_history( $post_id = 0, $message = null, $event = null ) { + + // Define local variable(s) + $user = ''; + + // Get the current user + $current_user = wp_get_current_user(); + + // Get the user's login name if possible + if ( is_object( $current_user ) && isset( $current_user->user_login ) ) + $user = $current_user->user_login; + + // Setup the event to be saved + $event = array( + 'time' => akismet_microtime(), + 'message' => $message, + 'event' => $event, + 'user' => $user, + ); + + // Save the event data + add_post_meta( $post_id, '_bbp_akismet_history', $event ); + } + + /** + * Get a post's Akismet history + * + * @since bbPress (r3308) + * + * @param int $post_id + * + * @uses wp_get_current_user() To get the current_user object + * @uses get_post_meta() Get a post's Akismet history + * + * @return array Array of a post's Akismet history + */ + public function get_post_history( $post_id = 0 ) { + + // Retrieve any previous history + $history = get_post_meta( $post_id, '_bbp_akismet_history' ); + + // Sort it by the time recorded + usort( $history, 'akismet_cmp_time' ); + + return $history; + } + + /** + * Handle any terms submitted with a post flagged as spam + * + * @since bbPress (r3308) + * + * @param string $terms Comma-separated list of terms + * @param int $topic_id + * @param int $reply_id + * + * @uses bbp_get_reply_id() To get the reply_id + * @uses bbp_get_topic_id() To get the topic_id + * @uses wp_get_object_terms() To a post's current terms + * @uses update_post_meta() To add spam terms to post meta + * + * @return array Array of existing topic terms + */ + public function filter_post_terms( $terms = '', $topic_id = 0, $reply_id = 0 ) { + + // Validate the reply_id and topic_id + $reply_id = bbp_get_reply_id( $reply_id ); + $topic_id = bbp_get_topic_id( $topic_id ); + + // Get any pre-existing terms + $existing_terms = wp_get_object_terms( $topic_id, bbp_get_topic_tag_tax_id(), array( 'fields' => 'names' ) ); + + // Save the terms for later in case the reply gets hammed + if ( !empty( $terms ) ) + update_post_meta( $reply_id, '_bbp_akismet_spam_terms', $terms ); + + // Keep the topic tags the same for now + return $existing_terms; + } + + /** + * Submit data to Akismet service with unique bbPress User Agent + * + * This code is directly taken from the akismet_http_post() function and + * documented to bbPress 2.0 standard. + * + * @since bbPress (r3466) + * + * @param string $request The request we are sending + * @param string $host The host to send our request to + * @param string $path The path from the host + * @param string $port The port to use + * @param string $ip Optional Override $host with an IP address + * @uses bbp_get_version() To get the current bbPress version + * @return mixed WP_Error on error, array on success, empty on failure + */ + private function http_post( $request, $host, $path, $port = 80, $ip = '' ) { + + // Preload required variables + $bbp_version = bbp_get_version(); + $content_length = strlen( $request ); + $http_host = $host; + $blog_charset = get_option( 'blog_charset' ); + $response = ''; + $errno = null; + $errstr = null; + + // Untque User Agent + $akismet_ua = "bbPress/{$bbp_version} | "; + $akismet_ua .= 'Akismet/' . constant( 'AKISMET_VERSION' ); + + // Use specific IP (if provided) + if ( !empty( $ip ) && long2ip( ip2long( $ip ) ) ) + $http_host = $ip; + + // WP HTTP class is available + if ( function_exists( 'wp_remote_post' ) ) { + + // Setup the arguments + $http_args = array( + 'body' => $request, + 'headers' => array( + 'Content-Type' => 'application/x-www-form-urlencoded; charset=' . $blog_charset, + 'Host' => $host, + 'User-Agent' => $akismet_ua + ), + 'httpversion' => '1.0', + 'timeout' => 15 + ); + + // Where we are sending our request + $akismet_url = 'http://' . $http_host . $path; + + // Send the request + $response = wp_remote_post( $akismet_url, $http_args ); + + // Bail if the response is an error + if ( is_wp_error( $response ) ) + return ''; + + // No errors so return response + return array( $response['headers'], $response['body'] ); + + // WP HTTP class is not available (Why not?) + } else { + + // Header info to use with our socket + $http_request = "POST {$path} HTTP/1.0\r\n"; + $http_request .= "Host: {$host}\r\n"; + $http_request .= "Content-Type: application/x-www-form-urlencoded; charset={$blog_charset}\r\n"; + $http_request .= "Content-Length: {$content_length}\r\n"; + $http_request .= "User-Agent: {$akismet_ua}\r\n"; + $http_request .= "\r\n"; + $http_request .= $request; + + // Open a socket connection + if ( false != ( $fs = @fsockopen( $http_host, $port, $errno, $errstr, 10 ) ) ) { + + // Write our request to the pointer + fwrite( $fs, $http_request ); + + // Loop through pointer and compile a response + while ( !feof( $fs ) ) { + // One TCP-IP packet at a time + $response .= fgets( $fs, 1160 ); + } + + // Close our socket + fclose( $fs ); + + // Explode the response into usable data + $response = explode( "\r\n\r\n", $response, 2 ); + } + + // Return the response ('' if error/empty) + return $response; + } + } +} +endif;