PluginProbe
bbPress / 2.6.17
bbPress v2.6.17
2.6.17 trunk 2.0 2.0-beta-1 2.0-beta-2b 2.0-beta-3 2.0-beta-3b 2.0-rc-2 2.0-rc-3 2.0-rc-4 2.0-rc-5 2.0.1 2.0.2 2.0.3 2.1 2.1-beta-1 2.1-rc1 2.1-rc2 2.1-rc3 2.1-rc4 2.1.1 2.1.2 2.1.3 2.2 2.2.1 All 72 releases
← All changes | includes/extend/akismet.php +862 -248 2.22.6.17 View file →
@@ -7,15 +7,15 @@
7 7 * @subpackage Akismet
8 8 */
9 9
10 10 // Exit if accessed directly
11 -if ( !defined( 'ABSPATH' ) ) exit;
11 +defined( 'ABSPATH' ) || exit;
12 12
13 -if ( !class_exists( 'BBP_Akismet' ) ) :
13 +if ( ! class_exists( 'BBP_Akismet' ) ) :
14 14 /**
15 15 * Loads Akismet extension
16 16 *
17 - * @since bbPress (r3277)
17 + * @since 2.0.0 bbPress (r3277)
18 18 *
19 19 * @package bbPress
20 20 * @subpackage Akismet
21 21 */
@@ -21,13 +21,20 @@
21 21 */
22 22 class BBP_Akismet {
23 23
24 24 /**
25 + * The last post checked by Akismet.
26 + *
27 + * @since 2.0.0 bbPress (r3277)
28 + *
29 + * @var array $last_post Default empty array.
30 + */
31 + protected $last_post = array();
32 +
33 + /**
25 34 * The main bbPress Akismet loader
26 35 *
27 - * @since bbPress (r3277)
28 - *
29 - * @uses add_filter()
36 + * @since 2.0.0 bbPress (r3277)
30 37 */
31 38 public function __construct() {
32 39 $this->setup_actions();
33 40 }
@@ -34,13 +41,11 @@
34 41
35 42 /**
36 43 * Setup the admin hooks
37 44 *
38 - * @since bbPress (r3277)
45 + * @since 2.0.0 bbPress (r3277)
46 + *
39 47 * @access private
40 - *
41 - * @uses add_filter() To add various filters
42 - * @uses add_action() To add various actions
43 48 */
44 49 private function setup_actions() {
45 50
46 51 // Prevent debug notices
@@ -62,66 +67,70 @@
62 67 'bbp_unspammed_reply' => 10, // Unspammed reply
63 68 );
64 69
65 70 // Add the checks
66 - foreach ( $checks as $type => $functions )
67 - foreach( $functions as $function => $priority )
71 + foreach ( $checks as $type => $functions ) {
72 + foreach ( $functions as $function => $priority ) {
68 73 add_filter( $function, array( $this, $type . '_post' ), $priority );
74 + }
75 + }
69 76
70 77 // Update post meta
71 78 add_action( 'wp_insert_post', array( $this, 'update_post_meta' ), 10, 2 );
79 +
80 + // Cleanup
81 + add_action( 'akismet_scheduled_delete', array( $this, 'delete_old_spam' ) );
82 + add_action( 'akismet_scheduled_delete', array( $this, 'delete_old_spam_meta' ) );
83 + add_action( 'akismet_scheduled_delete', array( $this, 'delete_orphaned_spam_meta' ) );
84 +
85 + // Admin
86 + if ( is_admin() ) {
87 + add_action( 'add_meta_boxes', array( $this, 'add_metaboxes' ) );
88 + }
72 89 }
73 90
74 91 /**
75 92 * Converts topic/reply data into Akismet comment checking format
76 93 *
77 - * @since bbPress (r3277)
94 + * @since 2.0.0 bbPress (r3277)
78 95 *
79 - * @param string $post_data
96 + * @param array $post_data
80 97 *
81 - * @uses get_userdata() To get the user data
82 - * @uses bbp_filter_anonymous_user_data() To get anonymous user data
83 - * @uses bbp_get_topic_permalink() To get the permalink of the topic
84 - * @uses bbp_get_reply_url() To get the permalink of the reply
85 - * @uses bbp_current_author_ip() To get the IP address of the current user
86 - * @uses BBP_Akismet::maybe_spam() To check if post is spam
87 - * @uses akismet_get_user_roles() To get the role(s) of the current user
88 - * @uses do_action() To call the 'bbp_akismet_spam_caught' hook
89 - * @uses add_filter() To call the 'bbp_new_reply_pre_set_terms' hook
90 - *
91 98 * @return array Array of post data
92 99 */
93 - public function check_post( $post_data ) {
100 + public function check_post( $post_data = array() ) {
94 101
95 102 // Define local variables
96 103 $user_data = array();
97 104 $post_permalink = '';
98 105
99 - // Post is not published
100 - if ( bbp_get_public_status_id() != $post_data['post_status'] )
101 - return $post_data;
102 -
103 106 // Cast the post_author to 0 if it's empty
104 - if ( empty( $post_data['post_author'] ) )
107 + if ( empty( $post_data['post_author'] ) ) {
105 108 $post_data['post_author'] = 0;
109 + }
106 110
107 111 /** Author ************************************************************/
108 112
113 + $user_data['last_active'] = '';
114 + $user_data['registered'] = date( 'Y-m-d H:i:s' ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
115 + $user_data['total_posts'] = (int) bbp_get_user_post_count( $post_data['post_author'] );
116 +
109 117 // Get user data
110 118 $userdata = get_userdata( $post_data['post_author'] );
111 119 $anonymous_data = bbp_filter_anonymous_post_data();
112 120
113 121 // Author is anonymous
114 - if ( !empty( $anonymous_data ) ) {
122 + if ( ! bbp_has_errors() ) {
115 123 $user_data['name'] = $anonymous_data['bbp_anonymous_name'];
116 - $user_data['email'] = $anonymous_data['bbp_anonymous_name'];
117 - $user_data['website'] = $anonymous_data['bbp_anonymous_name'];
124 + $user_data['email'] = $anonymous_data['bbp_anonymous_email'];
125 + $user_data['website'] = $anonymous_data['bbp_anonymous_website'];
118 126
119 127 // Author is logged in
120 - } elseif ( !empty( $userdata ) ) {
121 - $user_data['name'] = $userdata->display_name;
122 - $user_data['email'] = $userdata->user_email;
123 - $user_data['website'] = $userdata->user_url;
128 + } elseif ( ! empty( $userdata ) ) {
129 + $user_data['name'] = $userdata->display_name;
130 + $user_data['email'] = $userdata->user_email;
131 + $user_data['website'] = $userdata->user_url;
132 + $user_data['registered'] = $userdata->user_registered;
124 133
125 134 // Missing author data, so set some empty strings
126 135 } else {
127 136 $user_data['name'] = '';
@@ -130,63 +139,128 @@
130 139 }
131 140
132 141 /** Post **************************************************************/
133 142
134 - // Use post parent for permalink
135 - if ( !empty( $post_data['post_parent'] ) )
143 + if ( ! empty( $post_data['post_parent'] ) ) {
144 +
145 + // Use post parent for permalink
136 146 $post_permalink = get_permalink( $post_data['post_parent'] );
137 147
138 - // Put post_data back into usable array
139 - $_post = array(
140 - 'comment_author' => $user_data['name'],
141 - 'comment_author_email' => $user_data['email'],
142 - 'comment_author_url' => $user_data['website'],
143 - 'comment_content' => $post_data['post_content'],
144 - 'comment_post_ID' => $post_data['post_parent'],
145 - 'comment_type' => $post_data['post_type'],
146 - 'permalink' => $post_permalink,
147 - 'referrer' => $_SERVER['HTTP_REFERER'],
148 - 'user_agent' => $_SERVER['HTTP_USER_AGENT'],
149 - 'user_ID' => $post_data['post_author'],
150 - 'user_ip' => bbp_current_author_ip(),
151 - 'user_role' => akismet_get_user_roles( $post_data['post_author'] ),
148 + // Use post parent to get datetime of last reply on this topic
149 + $reply_id = bbp_get_topic_last_reply_id( $post_data['post_parent'] );
150 + if ( ! empty( $reply_id ) ) {
151 + $user_data['last_active'] = get_post_field( 'post_date', $reply_id );
152 + }
153 + }
154 +
155 + // Pass title & content together into comment content
156 + $_post_content = trim( $post_data['post_title'] . "\n\n" . $post_data['post_content'] );
157 +
158 + // Check if the post data is spammy...
159 + $_post = $this->maybe_spam(
160 + array(
161 + 'comment_author' => $user_data['name'],
162 + 'comment_author_email' => $user_data['email'],
163 + 'comment_author_url' => $user_data['website'],
164 + 'comment_content' => $_post_content,
165 + 'comment_post_ID' => $post_data['post_parent'],
166 + 'comment_type' => $post_data['post_type'],
167 + 'comment_total' => $user_data['total_posts'],
168 + 'comment_last_active_gmt' => $user_data['last_active'],
169 + 'comment_account_registered_gmt' => $user_data['registered'],
170 + 'permalink' => $post_permalink,
171 + 'referrer' => wp_get_raw_referer(),
172 + 'user_agent' => bbp_current_author_ua(),
173 + 'user_ID' => $post_data['post_author'],
174 + 'user_ip' => bbp_current_author_ip(),
175 + 'user_role' => $this->get_user_roles( $post_data['post_author'] ),
176 + )
152 177 );
153 178
154 - // Check the post_data
155 - $_post = $this->maybe_spam( $_post );
179 + // Set the results (from maybe_spam() above)
180 + $post_data['bbp_akismet_result_headers'] = $_post['bbp_akismet_result_headers'];
181 + $post_data['bbp_akismet_result'] = $_post['bbp_akismet_result'];
182 + $post_data['bbp_post_as_submitted'] = $_post;
156 183
157 - // Get the result
158 - $post_data['bbp_akismet_result'] = $_post['bbp_akismet_result'];
159 - unset( $_post['bbp_akismet_result'] );
184 + // Avoid recursion by unsetting results from post-as-submitted
185 + unset(
186 + $post_data['bbp_post_as_submitted']['bbp_akismet_result_headers'],
187 + $post_data['bbp_post_as_submitted']['bbp_akismet_result']
188 + );
160 189
161 - // Store the data as submitted
162 - $post_data['bbp_post_as_submitted'] = $_post;
190 + // Allow post_data to be manipulated
191 + $post_data = apply_filters( 'bbp_akismet_check_post', $post_data );
163 192
164 - // Allow post_data to be manipulated
165 - do_action_ref_array( 'bbp_akismet_check_post', $post_data );
193 + // Parse and log the last response
194 + $this->last_post = $this->parse_response( $post_data );
166 195
167 - // Spam
168 - if ( 'true' == $post_data['bbp_akismet_result'] ) {
196 + // Return the last response back to the filter
197 + return $this->last_post;
198 + }
169 199
200 + /**
201 + * Parse the response from the Akismet service, and alter the post data as
202 + * necessary. For example, switch the status to `spam` if spammy.
203 + *
204 + * Note: this method also is responsible for allowing users who can moderate to
205 + * never have their posts marked as spam. This is because they are "trusted"
206 + * users. However, their posts are still sent to Akismet to be checked.
207 + *
208 + * @since 2.6.0 bbPress (r6873)
209 + *
210 + * @param array $post_data
211 + *
212 + * @return array
213 + */
214 + private function parse_response( $post_data = array() ) {
215 +
216 + // Get the parent ID of the post as submitted
217 + $parent_id = ! empty( $post_data['bbp_post_as_submitted']['comment_post_ID'] )
218 + ? absint( $post_data['bbp_post_as_submitted']['comment_post_ID'] )
219 + : 0;
220 +
221 + // Allow moderators to skip spam (includes per-forum moderators via $parent_id)
222 + $skip_spam = current_user_can( 'moderate', $parent_id );
223 +
224 + // Bail early if current user can skip spam enforcement
225 + if ( apply_filters( 'bbp_bypass_spam_enforcement', $skip_spam, $post_data ) ) {
226 + return $post_data;
227 + }
228 +
229 + // Discard obvious spam
230 + if ( get_option( 'akismet_strictness' ) ) {
231 +
232 + // Akismet is 100% confident this is spam
233 + if (
234 + ! empty( $post_data['bbp_akismet_result_headers']['x-akismet-pro-tip'] )
235 + &&
236 + ( 'discard' === $post_data['bbp_akismet_result_headers']['x-akismet-pro-tip'] )
237 + ) {
238 +
239 + // URL to redirect to (current, or forum root)
240 + $redirect_to = ( ! empty( $_SERVER['HTTP_HOST'] ) && ! empty( $_SERVER['REQUEST_URI'] ) )
241 + ? bbp_get_url_scheme() . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']
242 + : bbp_get_root_url();
243 +
244 + // Do the redirect (post data not saved!)
245 + bbp_redirect( $redirect_to );
246 + }
247 + }
248 +
249 + // Result is spam, so set the status as such
250 + if ( 'true' === $post_data['bbp_akismet_result'] ) {
251 +
170 252 // Let plugins do their thing
171 253 do_action( 'bbp_akismet_spam_caught' );
172 254
173 - // This is spam
255 + // Set post_status to spam
174 256 $post_data['post_status'] = bbp_get_spam_status_id();
175 257
176 - // We don't want your spam tags here
258 + // Filter spammy tags into meta data
177 259 add_filter( 'bbp_new_reply_pre_set_terms', array( $this, 'filter_post_terms' ), 1, 3 );
178 -
179 - // @todo Spam counter?
180 260 }
181 261
182 - // @todo Topic/reply moderation? No true/false response - 'pending' or 'draft'
183 - // @todo Auto-delete old spam?
184 -
185 - // Log the last post
186 - $this->last_post = $post_data;
187 -
188 - // Pass the data back to the filter
262 + // Return the (potentially modified) post data
189 263 return $post_data;
190 264 }
191 265
192 266 /**
@@ -191,30 +265,17 @@
191 265
192 266 /**
193 267 * Submit a post for spamming or hamming
194 268 *
195 - * @since bbPress (r3277)
269 + * @since 2.0.0 bbPress (r3277)
196 270 *
197 271 * @param int $post_id
198 272 *
199 - * @global WP_Query $wpdb
200 273 * @global string $akismet_api_host
201 274 * @global string $akismet_api_port
202 275 * @global object $current_user
203 276 * @global object $current_site
204 277 *
205 - * @uses current_filter() To get the reply_id
206 - * @uses get_post() To get the post object
207 - * @uses get_the_author_meta() To get the author meta
208 - * @uses get_post_meta() To get the post meta
209 - * @uses bbp_get_user_profile_url() To get a user's profile url
210 - * @uses get_permalink() To get the permalink of the post_parent
211 - * @uses akismet_get_user_roles() To get the role(s) of the post_author
212 - * @uses bbp_current_author_ip() To get the IP address of the current user
213 - * @uses BBP_Akismet::maybe_spam() To submit the post as ham or spam
214 - * @uses update_post_meta() To update the post meta with some Akismet data
215 - * @uses do_action() To call the 'bbp_akismet_submit_spam_post' and 'bbp_akismet_submit_ham_post' hooks
216 - *
217 278 * @return array Array of existing topic terms
218 279 */
219 280 public function submit_post( $post_id = 0 ) {
220 281 global $current_user, $current_site;
@@ -249,15 +310,20 @@
249 310 // Make sure we have a post
250 311 $_post = get_post( $post_id );
251 312
252 313 // Bail if get_post() fails
253 - if ( empty( $_post ) )
314 + if ( empty( $_post ) ) {
254 315 return;
316 + }
255 317
256 318 // Bail if we're spamming, but the post_status isn't spam
257 - if ( ( 'spam' == $request_type ) && ( bbp_get_spam_status_id() != $_post->post_status ) )
319 + if ( ( 'spam' === $request_type ) && ( bbp_get_spam_status_id() !== $_post->post_status ) ) {
258 320 return;
321 + }
259 322
323 + // Pass title & content together into comment content
324 + $_post_content = trim( $_post->post_title . "\n\n" . $_post->post_content );
325 +
260 326 // Set some default post_data
261 327 $post_data = array(
262 328 'comment_approved' => $_post->post_status,
263 329 'comment_author' => $_post->post_author ? get_the_author_meta( 'display_name', $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_name', true ),
@@ -262,10 +328,10 @@
262 328 'comment_approved' => $_post->post_status,
263 329 'comment_author' => $_post->post_author ? get_the_author_meta( 'display_name', $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_name', true ),
264 330 'comment_author_email' => $_post->post_author ? get_the_author_meta( 'email', $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_email', true ),
265 331 'comment_author_url' => $_post->post_author ? bbp_get_user_profile_url( $_post->post_author ) : get_post_meta( $post_id, '_bbp_anonymous_website', true ),
266 - 'comment_content' => $_post->post_content,
267 - 'comment_date' => $_post->post_date,
332 + 'comment_content' => $_post_content,
333 + 'comment_date_gmt' => $_post->post_date_gmt,
268 334 'comment_ID' => $post_id,
269 335 'comment_post_ID' => $_post->post_parent,
270 336 'comment_type' => $_post->post_type,
271 337 'permalink' => get_permalink( $post_id ),
@@ -270,26 +336,29 @@
270 336 'comment_type' => $_post->post_type,
271 337 'permalink' => get_permalink( $post_id ),
272 338 'user_ID' => $_post->post_author,
273 339 'user_ip' => get_post_meta( $post_id, '_bbp_author_ip', true ),
274 - 'user_role' => akismet_get_user_roles( $_post->post_author ),
340 + 'user_role' => $this->get_user_roles( $_post->post_author ),
275 341 );
276 342
277 343 // Use the original version stored in post_meta if available
278 344 $as_submitted = get_post_meta( $post_id, '_bbp_akismet_as_submitted', true );
279 - if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) )
345 + if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) {
280 346 $post_data = array_merge( $post_data, $as_submitted );
347 + }
281 348
282 349 // Add the reporter IP address
283 350 $post_data['reporter_ip'] = bbp_current_author_ip();
284 351
285 352 // Add some reporter info
286 - if ( is_object( $current_user ) )
287 - $post_data['reporter'] = $current_user->user_login;
353 + if ( is_object( $current_user ) ) {
354 + $post_data['reporter'] = $current_user->user_login;
355 + }
288 356
289 357 // Add the current site domain
290 - if ( is_object( $current_site ) )
358 + if ( is_object( $current_site ) ) {
291 359 $post_data['site_domain'] = $current_site->domain;
360 + }
292 361
293 362 // Place your slide beneath the microscope
294 363 $post_data = $this->maybe_spam( $post_data, 'submit', $request_type );
295 364
@@ -300,16 +369,52 @@
300 369 switch ( $request_type ) {
301 370
302 371 // Spammy
303 372 case 'spam' :
304 - $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' );
373 + if ( 'topic' === $post_data['comment_type'] ) {
374 + /* translators: %s: reporter name */
375 + $message = sprintf( esc_html__( '%s reported this topic as spam', 'bbpress' ),
376 + $post_data['reporter']
377 + );
378 + } elseif ( 'reply' === $post_data['comment_type'] ) {
379 + /* translators: %s: reporter name */
380 + $message = sprintf( esc_html__( '%s reported this reply as spam', 'bbpress' ),
381 + $post_data['reporter']
382 + );
383 + } else {
384 + /* translators: 1: reporter name, 2: comment type */
385 + $message = sprintf( esc_html__( '%1$s reported this %2$s as spam', 'bbpress' ),
386 + $post_data['reporter'],
387 + $post_data['comment_type']
388 + );
389 + }
390 +
391 + $this->update_post_history( $post_id, $message, 'report-spam' );
305 392 update_post_meta( $post_id, '_bbp_akismet_user_result', 'true' );
306 393 update_post_meta( $post_id, '_bbp_akismet_user', $post_data['reporter'] );
307 394 break;
308 395
309 396 // Hammy
310 - case 'ham' :
311 - $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' );
397 + case 'ham' :
398 + if ( 'topic' === $post_data['comment_type'] ) {
399 + /* translators: %s: reporter name */
400 + $message = sprintf( esc_html__( '%s reported this topic as not spam', 'bbpress' ),
401 + $post_data['reporter']
402 + );
403 + } elseif ( 'reply' === $post_data['comment_type'] ) {
404 + /* translators: %s: reporter name */
405 + $message = sprintf( esc_html__( '%s reported this reply as not spam', 'bbpress' ),
406 + $post_data['reporter']
407 + );
408 + } else {
409 + /* translators: 1: reporter name, 2: comment type */
410 + $message = sprintf( esc_html__( '%1$s reported this %2$s as not spam', 'bbpress' ),
411 + $post_data['reporter'],
412 + $post_data['comment_type']
413 + );
414 + }
415 +
416 + $this->update_post_history( $post_id, $message, 'report-ham' );
312 417 update_post_meta( $post_id, '_bbp_akismet_user_result', 'false' );
313 418 update_post_meta( $post_id, '_bbp_akismet_user', $post_data['reporter'] );
314 419
315 420 // @todo Topic term revision history
@@ -326,9 +431,9 @@
326 431
327 432 /**
328 433 * Ping Akismet service and check for spam/ham response
329 434 *
330 - * @since bbPress (r3277)
435 + * @since 2.0.0 bbPress (r3277)
331 436 *
332 437 * @param array $post_data
333 438 * @param string $check Accepts check|submit
334 439 * @param string $spam Accepts spam|ham
@@ -335,73 +440,92 @@
335 440 *
336 441 * @global string $akismet_api_host
337 442 * @global string $akismet_api_port
338 443 *
339 - * @uses akismet_test_mode() To determine if Akismet is in test mode
340 - * @uses akismet_http_post() To send data to the mothership for processing
341 - *
342 444 * @return array Array of post data
343 445 */
344 - private function maybe_spam( $post_data, $check = 'check', $spam = 'spam' ) {
446 + private function maybe_spam( $post_data = array(), $check = 'check', $spam = 'spam' ) {
345 447 global $akismet_api_host, $akismet_api_port;
346 448
347 449 // Define variables
348 - $query_string = $path = $response = '';
450 + $query_string = $path = '';
451 + $response = array( '', '' );
349 452
453 + // Make sure post data is an array
454 + if ( ! is_array( $post_data ) ) {
455 + $post_data = array();
456 + }
457 +
350 458 // Populate post data
351 459 $post_data['blog'] = get_option( 'home' );
352 460 $post_data['blog_charset'] = get_option( 'blog_charset' );
353 461 $post_data['blog_lang'] = get_locale();
354 - $post_data['referrer'] = $_SERVER['HTTP_REFERER'];
355 - $post_data['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
462 + $post_data['referrer'] = wp_get_raw_referer();
463 + $post_data['user_agent'] = bbp_current_author_ua();
356 464
357 - // Akismet Test Mode
358 - if ( akismet_test_mode() )
359 - $post_data['is_test'] = 'true';
465 + // Loop through _POST args and rekey strings
466 + if ( ! empty( $_POST ) && is_countable( $_POST ) ) {
467 + foreach ( $_POST as $key => $value ) {
468 + if ( is_string( $value ) ) {
469 + $post_data[ 'POST_' . $key ] = $value;
470 + }
471 + }
472 + }
360 473
361 - // Loop through _POST args and rekey strings
362 - foreach ( $_POST as $key => $value )
363 - if ( is_string( $value ) )
364 - $post_data['POST_' . $key] = $value;
474 + // Loop through _SERVER args and remove allowed keys
475 + if ( ! empty( $_SERVER ) && is_countable( $_SERVER ) ) {
365 476
366 - // Keys to ignore
367 - $ignore = array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' );
477 + // Keys to ignore
478 + $ignore = array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' );
368 479
369 - // Loop through _SERVER args and remove whitelisted keys
370 - foreach ( $_SERVER as $key => $value ) {
480 + foreach ( $_SERVER as $key => $value ) {
371 481
372 - // Key should not be ignored
373 - if ( !in_array( $key, $ignore ) && is_string( $value ) ) {
374 - $post_data[$key] = $value;
482 + // Key should not be ignored
483 + if ( ! in_array( $key, $ignore, true ) && is_string( $value ) ) {
484 + $post_data[ $key ] = $value;
375 485
376 - // Key should be ignored
377 - } else {
378 - $post_data[$key] = '';
486 + // Key should be ignored
487 + } else {
488 + $post_data[ $key ] = '';
489 + }
379 490 }
380 491 }
381 492
382 - // Ready...
383 - foreach ( $post_data as $key => $data )
384 - $query_string .= $key . '=' . urlencode( stripslashes( $data ) ) . '&';
493 + // Encode post data
494 + if ( ! empty( $post_data ) && is_countable( $post_data ) ) {
495 + foreach ( $post_data as $key => $data ) {
496 + $query_string .= $key . '=' . urlencode( wp_unslash( $data ) ) . '&';
497 + }
498 + }
385 499
386 - // Aim...
387 - if ( 'check' == $check ) {
500 + // Only accepts spam|ham
501 + if ( ! in_array( $spam, array( 'spam', 'ham' ), true ) ) {
502 + $spam = 'spam';
503 + }
504 +
505 + // Setup the API route
506 + if ( 'check' === $check ) {
388 507 $path = '/1.1/comment-check';
389 - } elseif ( 'submit' == $check ) {
508 + } elseif ( 'submit' === $check ) {
390 509 $path = '/1.1/submit-' . $spam;
391 510 }
392 511
393 - // Fire!
394 - $response = $this->http_post( $query_string, $akismet_api_host, $path, $akismet_api_port );
512 + // Send data to Akismet
513 + if ( ! apply_filters( 'bbp_bypass_check_for_spam', false, $post_data ) ) {
514 + $response = $this->http_post( $query_string, $akismet_api_host, $path, $akismet_api_port );
515 + }
395 516
396 - // Check the high-speed cam
397 - if ( !empty( $response[1] ) ) {
398 - $post_data['bbp_akismet_result'] = $response[1];
399 - } else {
400 - $post_data['bbp_akismet_result'] = __( 'No response', 'bbpress' );
401 - }
517 + // Set the result headers
518 + $post_data['bbp_akismet_result_headers'] = ! empty( $response[0] )
519 + ? $response[0] // raw
520 + : esc_html__( 'No response', 'bbpress' );
402 521
403 - // This is ham
522 + // Set the result
523 + $post_data['bbp_akismet_result'] = ! empty( $response[1] )
524 + ? $response[1] // raw
525 + : esc_html__( 'No response', 'bbpress' );
526 +
527 + // Return the post data, with the results of the external Akismet request
404 528 return $post_data;
405 529 }
406 530
407 531 /**
@@ -406,20 +530,14 @@
406 530
407 531 /**
408 532 * Update post meta after a spam check
409 533 *
410 - * @since bbPress (r3308)
534 + * @since 2.0.0 bbPress (r3308)
411 535 *
412 536 * @param int $post_id
413 537 * @param object $_post
414 538 *
415 539 * @global object $this->last_post
416 - *
417 - * @uses get_post() To get the post object
418 - * @uses get_userdata() To get the user data
419 - * @uses bbp_filter_anonymous_user_data() To get anonymous user data
420 - * @uses update_post_meta() To update post meta with Akismet data
421 - * @uses BBP_Akismet::update_post_history() To update post Akismet history
422 540 */
423 541 public function update_post_meta( $post_id = 0, $_post = false ) {
424 542
425 543 // Define local variable(s)
@@ -428,54 +546,103 @@
428 546 // Setup some variables
429 547 $post_id = (int) $post_id;
430 548
431 549 // Ensure we have a post object
432 - if ( empty( $_post ) )
550 + if ( empty( $_post ) ) {
433 551 $_post = get_post( $post_id );
552 + }
434 553
435 554 // Set up Akismet last post data
436 - if ( !empty( $this->last_post ) )
555 + if ( ! empty( $this->last_post['bbp_post_as_submitted'] ) ) {
437 556 $as_submitted = $this->last_post['bbp_post_as_submitted'];
557 + }
438 558
439 559 // wp_insert_post() might be called in other contexts. Ensure this is
440 560 // the same topic/reply as was checked by BBP_Akismet::check_post()
441 - if ( is_object( $_post ) && !empty( $this->last_post ) && is_array( $as_submitted ) ) {
561 + if ( is_object( $_post ) && ! empty( $this->last_post ) && is_array( $as_submitted ) ) {
442 562
443 563 // Get user data
444 564 $userdata = get_userdata( $_post->post_author );
445 565 $anonymous_data = bbp_filter_anonymous_post_data();
446 566
567 + // Which name?
568 + $name = ! empty( $anonymous_data['bbp_anonymous_name'] )
569 + ? $anonymous_data['bbp_anonymous_name']
570 + : $userdata->display_name;
571 +
572 + // Which email?
573 + $email = ! empty( $anonymous_data['bbp_anonymous_email'] )
574 + ? $anonymous_data['bbp_anonymous_email']
575 + : $userdata->user_email;
576 +
447 577 // More checks
448 - if ( intval( $as_submitted['comment_post_ID'] ) == intval( $_post->post_parent )
449 - && $as_submitted['comment_author'] == ( $anonymous_data ? $anonymous_data['bbp_anonymous_name'] : $userdata->display_name )
450 - && $as_submitted['comment_author_email'] == ( $anonymous_data ? $anonymous_data['bbp_anonymous_email'] : $userdata->user_email )
451 - ) {
578 + if (
452 579
580 + // Post matches
581 + ( intval( $as_submitted['comment_post_ID'] ) === intval( $_post->post_parent ) )
582 +
583 + &&
584 +
585 + // Name matches
586 + ( $as_submitted['comment_author'] === $name )
587 +
588 + &&
589 +
590 + // Email matches
591 + ( $as_submitted['comment_author_email'] === $email )
592 + ) {
593 +
594 + // Delete old content daily
595 + if ( ! wp_next_scheduled( 'akismet_scheduled_delete' ) ) {
596 + wp_schedule_event( time(), 'daily', 'akismet_scheduled_delete' );
597 + }
598 +
453 599 // Normal result: true
454 - if ( $this->last_post['bbp_akismet_result'] == 'true' ) {
600 + if ( ! empty( $this->last_post['bbp_akismet_result'] ) && ( 'true' === $this->last_post['bbp_akismet_result'] ) ) {
455 601
456 602 // Leave a trail so other's know what we did
457 603 update_post_meta( $post_id, '_bbp_akismet_result', 'true' );
458 - $this->update_post_history( $post_id, __( 'Akismet caught this post as spam', 'bbpress' ), 'check-spam' );
604 + $this->update_post_history(
605 + $post_id,
606 + esc_html__( 'Akismet caught this post as spam', 'bbpress' ),
607 + 'check-spam'
608 + );
459 609
460 610 // If post_status isn't the spam status, as expected, leave a note
461 - if ( $_post->post_status != bbp_get_spam_status_id() ) {
462 - $this->update_post_history( $post_id, sprintf( __( 'Post status was changed to %s', 'bbpress' ), $_post->post_status ), 'status-changed-' . $_post->post_status );
611 + if ( bbp_get_spam_status_id() !== $_post->post_status ) {
612 + $this->update_post_history(
613 + $post_id,
614 + sprintf(
615 + /* translators: %s: Post status */
616 + esc_html__( 'Post status was changed to %s', 'bbpress' ),
617 + $_post->post_status
618 + ),
619 + 'status-changed-' . $_post->post_status
620 + );
463 621 }
464 622
465 623 // Normal result: false
466 - } elseif ( $this->last_post['bbp_akismet_result'] == 'false' ) {
624 + } elseif ( ! empty( $this->last_post['bbp_akismet_result'] ) && ( 'false' === $this->last_post['bbp_akismet_result'] ) ) {
467 625
468 626 // Leave a trail so other's know what we did
469 627 update_post_meta( $post_id, '_bbp_akismet_result', 'false' );
470 - $this->update_post_history( $post_id, __( 'Akismet cleared this post', 'bbpress' ), 'check-ham' );
628 + $this->update_post_history(
629 + $post_id,
630 + esc_html__( 'Akismet cleared this post as not spam', 'bbpress' ),
631 + 'check-ham'
632 + );
471 633
472 634 // If post_status is the spam status, which isn't expected, leave a note
473 - if ( $_post->post_status == bbp_get_spam_status_id() ) {
474 -
475 - // @todo Use wp_blacklist_check()
476 -
477 - $this->update_post_history( $post_id, sprintf( __( 'Post status was changed to %s', 'bbpress' ), $_post->post_status ), 'status-changed-' . $_post->post_status );
635 + if ( bbp_get_spam_status_id() === $_post->post_status ) {
636 + $this->update_post_history(
637 + $post_id,
638 + sprintf(
639 + /* translators: %s: Post status */
640 + esc_html__( 'Post status was changed to %s', 'bbpress' ),
641 + $_post->post_status
642 + ),
643 + 'status-changed-' . $_post->post_status
644 + );
478 645 }
479 646
480 647 // Abnormal result: error
481 648 } else {
@@ -480,14 +647,26 @@
480 647 // Abnormal result: error
481 648 } else {
482 649 // Leave a trail so other's know what we did
483 650 update_post_meta( $post_id, '_bbp_akismet_error', time() );
484 - $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' );
651 + $this->update_post_history(
652 + $post_id,
653 + sprintf(
654 + /* translators: %s: Akismet response */
655 + esc_html__( 'Akismet was unable to check this post (response: %s), will automatically retry again later.', 'bbpress' ),
656 + $this->last_post['bbp_akismet_result']
657 + ),
658 + 'check-error'
659 + );
485 660 }
486 661
487 662 // Record the complete original data as submitted for checking
488 663 if ( isset( $this->last_post['bbp_post_as_submitted'] ) ) {
489 - update_post_meta( $post_id, '_bbp_akismet_as_submitted', $this->last_post['bbp_post_as_submitted'] );
664 + update_post_meta(
665 + $post_id,
666 + '_bbp_akismet_as_submitted',
667 + $this->last_post['bbp_post_as_submitted']
668 + );
490 669 }
491 670 }
492 671 }
493 672 }
@@ -492,18 +671,15 @@
492 671 }
493 672 }
494 673
495 674 /**
496 - * Update a post's Akismet history
675 + * Update Akismet history of a Post
497 676 *
498 - * @since bbPress (r3308)
677 + * @since 2.0.0 bbPress (r3308)
499 678 *
500 679 * @param int $post_id
501 680 * @param string $message
502 681 * @param string $event
503 - *
504 - * @uses wp_get_current_user() To get the current_user object
505 - * @uses add_post_meta() Add Akismet post history
506 682 */
507 683 private function update_post_history( $post_id = 0, $message = null, $event = null ) {
508 684
509 685 // Define local variable(s)
@@ -512,14 +688,19 @@
512 688 // Get the current user
513 689 $current_user = wp_get_current_user();
514 690
515 691 // Get the user's login name if possible
516 - if ( is_object( $current_user ) && isset( $current_user->user_login ) )
692 + if ( is_object( $current_user ) && isset( $current_user->user_login ) ) {
517 693 $user = $current_user->user_login;
694 + }
518 695
696 + // This used to be akismet_microtime() but it was removed in 3.0
697 + $mtime = explode( ' ', microtime() );
698 + $message_time = $mtime[1] + $mtime[0];
699 +
519 700 // Setup the event to be saved
520 701 $event = array(
521 - 'time' => akismet_microtime(),
702 + 'time' => $message_time,
522 703 'message' => $message,
523 704 'event' => $event,
524 705 'user' => $user,
525 706 );
@@ -528,18 +709,15 @@
528 709 add_post_meta( $post_id, '_bbp_akismet_history', $event );
529 710 }
530 711
531 712 /**
532 - * Get a post's Akismet history
713 + * Get the Akismet history of a Post
533 714 *
534 - * @since bbPress (r3308)
715 + * @since 2.0.0 bbPress (r3308)
535 716 *
536 717 * @param int $post_id
537 718 *
538 - * @uses wp_get_current_user() To get the current_user object
539 - * @uses get_post_meta() Get a post's Akismet history
540 - *
541 - * @return array Array of a post's Akismet history
719 + * @return array Array of Akismet history
542 720 */
543 721 public function get_post_history( $post_id = 0 ) {
544 722
545 723 // Retrieve any previous history
@@ -553,19 +731,14 @@
553 731
554 732 /**
555 733 * Handle any terms submitted with a post flagged as spam
556 734 *
557 - * @since bbPress (r3308)
735 + * @since 2.0.0 bbPress (r3308)
558 736 *
559 737 * @param string $terms Comma-separated list of terms
560 738 * @param int $topic_id
561 739 * @param int $reply_id
562 740 *
563 - * @uses bbp_get_reply_id() To get the reply_id
564 - * @uses bbp_get_topic_id() To get the topic_id
565 - * @uses wp_get_object_terms() To a post's current terms
566 - * @uses update_post_meta() To add spam terms to post meta
567 - *
568 741 * @return array Array of existing topic terms
569 742 */
570 743 public function filter_post_terms( $terms = '', $topic_id = 0, $reply_id = 0 ) {
571 744
@@ -573,13 +746,14 @@
573 746 $reply_id = bbp_get_reply_id( $reply_id );
574 747 $topic_id = bbp_get_topic_id( $topic_id );
575 748
576 749 // Get any pre-existing terms
577 - $existing_terms = wp_get_object_terms( $topic_id, bbp_get_topic_tag_tax_id(), array( 'fields' => 'names' ) );
750 + $existing_terms = bbp_get_topic_tag_names( $topic_id );
578 751
579 752 // Save the terms for later in case the reply gets hammed
580 - if ( !empty( $terms ) )
753 + if ( ! empty( $terms ) ) {
581 754 update_post_meta( $reply_id, '_bbp_akismet_spam_terms', $terms );
755 + }
582 756
583 757 // Keep the topic tags the same for now
584 758 return $existing_terms;
585 759 }
@@ -589,9 +763,9 @@
589 763 *
590 764 * This code is directly taken from the akismet_http_post() function and
591 765 * documented to bbPress 2.0 standard.
592 766 *
593 - * @since bbPress (r3466)
767 + * @since 2.0.0 bbPress (r3466)
594 768 *
595 769 * @param string $request The request we are sending
596 770 * @param string $host The host to send our request to
597 771 * @param string $path The path from the host
@@ -596,91 +770,531 @@
596 770 * @param string $host The host to send our request to
597 771 * @param string $path The path from the host
598 772 * @param string $port The port to use
599 773 * @param string $ip Optional Override $host with an IP address
600 - * @uses bbp_get_version() To get the current bbPress version
601 774 * @return mixed WP_Error on error, array on success, empty on failure
602 775 */
603 776 private function http_post( $request, $host, $path, $port = 80, $ip = '' ) {
604 777
605 778 // Preload required variables
606 - $bbp_version = bbp_get_version();
607 - $content_length = strlen( $request );
608 - $http_host = $host;
609 - $blog_charset = get_option( 'blog_charset' );
610 - $response = '';
611 - $errno = null;
612 - $errstr = null;
779 + $bbp_version = bbp_get_version();
780 + $ak_version = constant( 'AKISMET_VERSION' );
781 + $http_host = $host;
782 + $blog_charset = get_option( 'blog_charset' );
613 783
614 - // Untque User Agent
615 - $akismet_ua = "bbPress/{$bbp_version} | ";
616 - $akismet_ua .= 'Akismet/' . constant( 'AKISMET_VERSION' );
784 + // User Agent & Content Type
785 + $akismet_ua = "bbPress/{$bbp_version} | Akismet/{$ak_version}";
786 + $content_type = 'application/x-www-form-urlencoded; charset=' . $blog_charset;
617 787
618 788 // Use specific IP (if provided)
619 - if ( !empty( $ip ) && long2ip( ip2long( $ip ) ) )
789 + if ( ! empty( $ip ) && long2ip( ip2long( $ip ) ) ) {
620 790 $http_host = $ip;
791 + }
621 792
622 - // WP HTTP class is available
623 - if ( function_exists( 'wp_remote_post' ) ) {
793 + // Setup the arguments
794 + $http_args = array(
795 + 'httpversion' => '1.0',
796 + 'timeout' => 15,
797 + 'body' => $request,
798 + 'headers' => array(
799 + 'Content-Type' => $content_type,
800 + 'Host' => $host,
801 + 'User-Agent' => $akismet_ua
802 + )
803 + );
624 804
625 - // Setup the arguments
626 - $http_args = array(
627 - 'body' => $request,
628 - 'headers' => array(
629 - 'Content-Type' => 'application/x-www-form-urlencoded; charset=' . $blog_charset,
630 - 'Host' => $host,
631 - 'User-Agent' => $akismet_ua
632 - ),
633 - 'httpversion' => '1.0',
634 - 'timeout' => 15
635 - );
805 + // Return the response
806 + return $this->get_response( $http_host . $path, $http_args );
807 + }
636 808
637 - // Where we are sending our request
638 - $akismet_url = 'http://' . $http_host . $path;
809 + /**
810 + * Handles the repeated calls to wp_remote_post(), including SSL support.
811 + *
812 + * @since 2.6.7 (bbPress r7194)
813 + *
814 + * @param string $host_and_path Scheme-less URL
815 + * @param array $http_args Array of arguments for wp_remote_post()
816 + * @return array
817 + */
818 + private function get_response( $host_and_path = '', $http_args = array() ) {
639 819
640 - // Send the request
641 - $response = wp_remote_post( $akismet_url, $http_args );
820 + // Default variables
821 + $akismet_url = $http_akismet_url = 'http://' . $host_and_path;
822 + $is_ssl = $ssl_failed = false;
823 + $now = time();
642 824
643 - // Bail if the response is an error
644 - if ( is_wp_error( $response ) )
645 - return '';
825 + // Check if SSL requests were disabled fewer than 24 hours ago
826 + $ssl_disabled_time = get_option( 'akismet_ssl_disabled' );
646 827
647 - // No errors so return response
648 - return array( $response['headers'], $response['body'] );
828 + // Clean-up if 24 hours have passed
829 + if ( ! empty( $ssl_disabled_time ) && ( $ssl_disabled_time < ( $now - DAY_IN_SECONDS ) ) ) {
830 + delete_option( 'akismet_ssl_disabled' );
831 + $ssl_disabled_time = false;
832 + }
649 833
650 - // WP HTTP class is not available (Why not?)
651 - } else {
834 + // Maybe HTTPS if not disabled
835 + if ( empty( $ssl_disabled_time ) ) {
836 + $is_ssl = wp_http_supports( array( 'ssl' ) );
652 837
653 - // Header info to use with our socket
654 - $http_request = "POST {$path} HTTP/1.0\r\n";
655 - $http_request .= "Host: {$host}\r\n";
656 - $http_request .= "Content-Type: application/x-www-form-urlencoded; charset={$blog_charset}\r\n";
657 - $http_request .= "Content-Length: {$content_length}\r\n";
658 - $http_request .= "User-Agent: {$akismet_ua}\r\n";
659 - $http_request .= "\r\n";
660 - $http_request .= $request;
838 + // Use SSL
839 + if ( ! empty( $is_ssl ) ) {
840 + $akismet_url = set_url_scheme( $akismet_url, 'https' );
841 + }
842 + }
661 843
662 - // Open a socket connection
663 - if ( false != ( $fs = @fsockopen( $http_host, $port, $errno, $errstr, 10 ) ) ) {
844 + // Initial remote request
845 + $response = wp_remote_post( $akismet_url, $http_args );
664 846
665 - // Write our request to the pointer
666 - fwrite( $fs, $http_request );
847 + // Initial request produced an error, so retry...
848 + if ( ! empty( $is_ssl ) && is_wp_error( $response ) ) {
667 849
668 - // Loop through pointer and compile a response
669 - while ( !feof( $fs ) ) {
670 - // One TCP-IP packet at a time
671 - $response .= fgets( $fs, 1160 );
850 + // Intermittent connection problems may cause the first HTTPS
851 + // request to fail and subsequent HTTP requests to succeed randomly.
852 + // Retry the HTTPS request once before disabling SSL for a time.
853 + $response = wp_remote_post( $akismet_url, $http_args );
854 +
855 + // SSL request failed twice, so try again without it
856 + if ( is_wp_error( $response ) ) {
857 + $response = wp_remote_post( $http_akismet_url, $http_args );
858 + $ssl_failed = true;
859 + }
860 + }
861 +
862 + // Bail if errored
863 + if ( is_wp_error( $response ) ) {
864 + return array( '', '' );
865 + }
866 +
867 + // Maybe disable SSL for future requests
868 + if ( ! empty( $ssl_failed ) ) {
869 + update_option( 'akismet_ssl_disabled', $now );
870 + }
871 +
872 + // No errors so return response
873 + return array(
874 + $response['headers'],
875 + $response['body']
876 + );
877 + }
878 +
879 + /**
880 + * Return a user's roles on this site (including super_admin)
881 + *
882 + * @since 2.3.0 bbPress (r4812)
883 + *
884 + * @param int $user_id
885 + *
886 + * @return boolean
887 + */
888 + private function get_user_roles( $user_id = 0 ) {
889 +
890 + // Default return value
891 + $roles = array();
892 +
893 + // Bail if cannot query the user
894 + if ( ! class_exists( 'WP_User' ) || empty( $user_id ) ) {
895 + return false;
896 + }
897 +
898 + // User ID
899 + $user = new WP_User( $user_id );
900 + if ( isset( $user->roles ) ) {
901 + $roles = (array) $user->roles;
902 + }
903 +
904 + // Super admin
905 + if ( is_multisite() && is_super_admin( $user_id ) ) {
906 + $roles[] = 'super_admin';
907 + }
908 +
909 + return implode( ',', $roles );
910 + }
911 +
912 + /** Admin *****************************************************************/
913 +
914 + /**
915 + * Add Aksimet History meta-boxes to topics and replies
916 + *
917 + * @since 2.4.0 bbPress (r5049)
918 + */
919 + public function add_metaboxes() {
920 +
921 + // Topics
922 + add_meta_box(
923 + 'bbp_akismet_topic_history',
924 + __( 'Akismet History', 'bbpress' ),
925 + array( $this, 'history_metabox' ),
926 + bbp_get_topic_post_type(),
927 + 'normal',
928 + 'core'
929 + );
930 +
931 + // Replies
932 + add_meta_box(
933 + 'bbp_akismet_reply_history',
934 + __( 'Akismet History', 'bbpress' ),
935 + array( $this, 'history_metabox' ),
936 + bbp_get_reply_post_type(),
937 + 'normal',
938 + 'core'
939 + );
940 + }
941 +
942 + /**
943 + * Output for Akismet History meta-box
944 + *
945 + * @since 2.4.0 bbPress (r5049)
946 + */
947 + public function history_metabox() {
948 +
949 + // Post ID
950 + $history = $this->get_post_history( get_the_ID() ); ?>
951 +
952 + <div class="akismet-history" style="margin: 13px 0;">
953 +
954 + <?php if ( ! empty( $history ) ) : ?>
955 +
956 + <table>
957 + <tbody>
958 +
959 + <?php foreach ( $history as $row ) : ?>
960 +
961 + <tr>
962 + <td style="color: #999; text-align: right; white-space: nowrap;">
963 + <span title="<?php echo esc_attr( date( 'D d M Y @ h:i:m a', $row['time'] ) . ' GMT' ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date ?>">
964 + <?php bbp_time_since( $row['time'], false, true ); ?>
965 + </span>
966 + </td>
967 + <td style="padding-left: 5px;">
968 + <?php echo esc_html( $row['message'] ); ?>
969 + </td>
970 + </tr>
971 +
972 + <?php endforeach; ?>
973 + </tbody>
974 + </table>
975 +
976 + <?php else : ?>
977 +
978 + <p><?php esc_html_e( 'No recorded history. Akismet has not checked this post.', 'bbpress' ); ?></p>
979 +
980 + <?php endif; ?>
981 +
982 + </div>
983 +
984 + <?php
985 + }
986 +
987 + /**
988 + * Get the number of rows to delete in a single clean-up query.
989 + *
990 + * @since 2.6.9 bbPress (r7225)
991 + *
992 + * @param string $filter The name of the filter to run.
993 + * @return int
994 + */
995 + public function get_delete_limit( $filter = '' ) {
996 +
997 + // Default filter
998 + if ( empty( $filter ) ) {
999 + $filter = '_bbp_akismet_delete_spam_limit';
1000 + }
1001 +
1002 + /**
1003 + * Determines how many rows will be deleted in each batch.
1004 + *
1005 + * @param int The number of rows. Default 1000.
1006 + */
1007 + $delete_limit = (int) apply_filters( $filter, 1000 );
1008 +
1009 + // Validate and return the deletion limit
1010 + return max( 1, $delete_limit );
1011 + }
1012 +
1013 + /**
1014 + * Get the interval (in days) for spam to remain in the queue.
1015 + *
1016 + * @since 2.6.9 bbPress (r7225)
1017 + *
1018 + * @param string $filter The name of the filter to run.
1019 + * @return int
1020 + */
1021 + public function get_delete_interval( $filter = '' ) {
1022 +
1023 + // Default filter
1024 + if ( empty( $filter ) ) {
1025 + $filter = '_bbp_akismet_delete_spam_interval';
1026 + }
1027 +
1028 + /**
1029 + * Determines how many days a piece of spam will be left in the Spam
1030 + * queue before being deleted.
1031 + *
1032 + * @param int The number of days. Default 15.
1033 + */
1034 + $delete_interval = (int) apply_filters( $filter, 15 );
1035 +
1036 + // Validate and return the deletion interval
1037 + return max( 1, $delete_interval );
1038 + }
1039 +
1040 + /**
1041 + * Deletes old spam topics & replies from the queue after 15 days
1042 + * (determined by `_bbp_akismet_delete_spam_interval` filter)
1043 + * since they are not useful in the long term.
1044 + *
1045 + * @since 2.6.7 bbPress (r7203)
1046 + *
1047 + * @global wpdb $wpdb
1048 + */
1049 + public function delete_old_spam() {
1050 + global $wpdb;
1051 +
1052 + // Get the deletion limit & interval
1053 + $delete_limit = $this->get_delete_limit( '_bbp_akismet_delete_spam_limit' );
1054 + $delete_interval = $this->get_delete_interval( '_bbp_akismet_delete_spam_interval' );
1055 +
1056 + // Setup the query
1057 + $sql = "SELECT id FROM {$wpdb->posts} WHERE post_type IN ('topic', 'reply') AND post_status = 'spam' AND DATE_SUB(NOW(), INTERVAL %d DAY) > post_date_gmt LIMIT %d";
1058 +
1059 + // Query loop of topic & reply IDs
1060 + while ( $spam_ids = $wpdb->get_col( $wpdb->prepare( $sql, $delete_interval, $delete_limit ) ) ) { // phpcs:ignore
1061 +
1062 + // Exit loop if no spam IDs
1063 + if ( empty( $spam_ids ) ) {
1064 + break;
1065 + }
1066 +
1067 + // Reset queries
1068 + $wpdb->queries = array();
1069 +
1070 + // Loop through each of the topic/reply IDs
1071 + foreach ( $spam_ids as $spam_id ) {
1072 +
1073 + /**
1074 + * Perform a single action on the single topic/reply ID for
1075 + * simpler batch processing.
1076 + *
1077 + * Maybe we should run the bbp_delete_topic or bbp_delete_reply
1078 + * actions here, too?
1079 + *
1080 + * @param string The current function.
1081 + * @param int The current topic/reply ID.
1082 + */
1083 + do_action( '_bbp_akismet_batch_delete', __FUNCTION__, $spam_id );
1084 + }
1085 +
1086 + // phpcs:disable
1087 +
1088 + // Prepared as strings since id is an unsigned BIGINT, and using %
1089 + // will constrain the value to the maximum signed BIGINT.
1090 + $format_string = implode( ', ', array_fill( 0, count( $spam_ids ), '%s' ) );
1091 +
1092 + // Run the delete queries
1093 + $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->posts} WHERE ID IN ( {$format_string} )", $spam_ids ) );
1094 + $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE post_id IN ( {$format_string} )", $spam_ids ) );
1095 +
1096 + // Clean the post cache for these topics & replies
1097 + clean_post_cache( $spam_ids );
1098 +
1099 + /**
1100 + * Single action that encompasses all topic/reply IDs after the
1101 + * delete queries have been run.
1102 + *
1103 + * @param int Count of topic/reply IDs
1104 + * @param array Array of topic/reply IDs
1105 + */
1106 + do_action( '_bbp_akismet_delete_spam_count', count( $spam_ids ), $spam_ids );
1107 + }
1108 +
1109 + /**
1110 + * Determines whether tables should be optimized.
1111 + *
1112 + * @param int Random number between 1 and 5000.
1113 + */
1114 + $optimize = (int) apply_filters( '_bbp_akismet_optimize_tables', mt_rand( 1, 5000 ), array( $wpdb->posts, $wpdb->postmeta ) );
1115 +
1116 + // Lucky number 11
1117 + if ( 11 === $optimize ) {
1118 + $wpdb->query( "OPTIMIZE TABLE {$wpdb->posts}" );
1119 + $wpdb->query( "OPTIMIZE TABLE {$wpdb->postmeta}" );
1120 + }
1121 + }
1122 +
1123 + /**
1124 + * Deletes `_bbp_akismet_as_submitted` meta keys after 15 days
1125 + * (determined by `_bbp_akismet_delete_spam_meta_interval` filter)
1126 + * since they are large and not useful in the long term.
1127 + *
1128 + * @since 2.6.7 bbPress (r7203)
1129 + *
1130 + * @global wpdb $wpdb
1131 + */
1132 + public function delete_old_spam_meta() {
1133 + global $wpdb;
1134 +
1135 + // Get the deletion limit & interval
1136 + $delete_limit = $this->get_delete_limit( '_bbp_akismet_delete_spam_meta_limit' );
1137 + $delete_interval = $this->get_delete_interval( '_bbp_akismet_delete_spam_meta_interval' );
1138 +
1139 + // Setup the query
1140 + $sql = "SELECT m.post_id FROM {$wpdb->postmeta} as m INNER JOIN {$wpdb->posts} as p ON m.post_id = p.ID WHERE m.meta_key = '_bbp_akismet_as_submitted' AND DATE_SUB(NOW(), INTERVAL %d DAY) > p.post_date_gmt LIMIT %d";
1141 +
1142 + // Query loop of topic & reply IDs
1143 + while ( $spam_ids = $wpdb->get_col( $wpdb->prepare( $sql, $delete_interval, $delete_limit ) ) ) { // phpcs:ignore
1144 +
1145 + // Exit loop if no spam IDs
1146 + if ( empty( $spam_ids ) ) {
1147 + break;
1148 + }
1149 +
1150 + // Reset queries
1151 + $wpdb->queries = array();
1152 +
1153 + // Loop through each of the topic/reply IDs
1154 + foreach ( $spam_ids as $spam_id ) {
1155 +
1156 + // Delete the as_submitted meta data
1157 + delete_post_meta( $spam_id, '_bbp_akismet_as_submitted' );
1158 +
1159 + /**
1160 + * Perform a single action on the single topic/reply ID for
1161 + * simpler batch processing.
1162 + *
1163 + * @param string The current function.
1164 + * @param int The current topic/reply ID.
1165 + */
1166 + do_action( '_bbp_akismet_batch_delete', __FUNCTION__, $spam_id );
1167 + }
1168 +
1169 + /**
1170 + * Single action that encompasses all topic/reply IDs after the
1171 + * delete queries have been run.
1172 + *
1173 + * @param int Count of topic/reply IDs
1174 + * @param array Array of topic/reply IDs
1175 + */
1176 + do_action( '_bbp_akismet_delete_spam_meta_count', count( $spam_ids ), $spam_ids );
1177 + }
1178 +
1179 + // Maybe optimize
1180 + $this->maybe_optimize_postmeta();
1181 + }
1182 +
1183 + /**
1184 + * Clears post meta that no longer has corresponding posts in the database
1185 + * (determined by `_bbp_akismet_delete_spam_orphaned_limit` filter)
1186 + * since it is not useful in the long term.
1187 + *
1188 + * @since 2.6.7 bbPress (r7203)
1189 + *
1190 + * @global wpdb $wpdb
1191 + */
1192 + public function delete_orphaned_spam_meta() {
1193 + global $wpdb;
1194 +
1195 + // Get the deletion limit
1196 + $delete_limit = $this->get_delete_limit( '_bbp_akismet_delete_spam_orphaned_limit' );
1197 +
1198 + // Default last meta ID
1199 + $last_meta_id = 0;
1200 +
1201 + // Start time (float)
1202 + $start_time = isset( $_SERVER['REQUEST_TIME_FLOAT'] )
1203 + ? (float) $_SERVER['REQUEST_TIME_FLOAT']
1204 + : microtime( true );
1205 +
1206 + // Maximum time
1207 + $max_exec_time = (float) max( ini_get( 'max_execution_time' ) - 5, 3 );
1208 +
1209 + // Setup the query
1210 + $sql = 'SELECT m.meta_id, m.post_id, m.meta_key'
1211 + . " FROM {$wpdb->postmeta} as m"
1212 + . " LEFT JOIN {$wpdb->posts} as p"
1213 + . ' ON m.post_id = p.ID'
1214 + . ' WHERE p.ID IS NULL'
1215 + . ' AND m.meta_id > %d'
1216 + . " AND m.meta_key LIKE 'akismet\\_%'"
1217 + . ' ORDER BY m.meta_id'
1218 + . ' LIMIT %d';
1219 +
1220 + // Query loop of topic & reply IDs
1221 + while ( $spam_meta_results = $wpdb->get_results( $wpdb->prepare( $sql, $last_meta_id, $delete_limit ) ) ) { // phpcs:ignore
1222 +
1223 + // Exit loop if no spam IDs
1224 + if ( empty( $spam_meta_results ) ) {
1225 + break;
1226 + }
1227 +
1228 + // Reset queries
1229 + $wpdb->queries = array();
1230 +
1231 + // Reset deleted meta count
1232 + $spam_meta_deleted = array();
1233 +
1234 + // Loop through each of the metas
1235 + foreach ( $spam_meta_results as $spam_meta ) {
1236 +
1237 + // Skip if not an Akismet key
1238 + if ( 'akismet_' !== substr( $spam_meta->meta_key, 0, 8 ) ) {
1239 + continue;
672 1240 }
673 1241
674 - // Close our socket
675 - fclose( $fs );
1242 + // Delete the meta
1243 + delete_post_meta( $spam_meta->post_id, $spam_meta->meta_key );
676 1244
677 - // Explode the response into usable data
678 - $response = explode( "\r\n\r\n", $response, 2 );
1245 + /**
1246 + * Perform a single action on the single topic/reply ID for
1247 + * simpler batch processing.
1248 + *
1249 + * @param string The current function.
1250 + * @param int The current topic/reply ID.
1251 + */
1252 + do_action( '_bbp_akismet_batch_delete', __FUNCTION__, $spam_meta );
1253 +
1254 + // Stash the meta ID being deleted
1255 + $spam_meta_deleted[] = $last_meta_id = $spam_meta->meta_id;
679 1256 }
680 1257
681 - // Return the response ('' if error/empty)
682 - return $response;
1258 + /**
1259 + * Single action that encompasses all topic/reply IDs after the
1260 + * delete queries have been run.
1261 + *
1262 + * @param int Count of spam meta IDs
1263 + * @param array Array of spam meta IDs
1264 + */
1265 + do_action( '_bbp_akismet_delete_spam_meta_count', count( $spam_meta_deleted ), $spam_meta_deleted );
1266 +
1267 + // Break if getting close to max_execution_time.
1268 + if ( ( microtime( true ) - $start_time ) > $max_exec_time ) {
1269 + break;
1270 + }
1271 + }
1272 +
1273 + // Maybe optimize
1274 + $this->maybe_optimize_postmeta();
1275 + }
1276 +
1277 + /**
1278 + * Maybe OPTIMIZE the _postmeta database table.
1279 + *
1280 + * @since 2.7.0 bbPress (r7203)
1281 + *
1282 + * @global wpdb $wpdb
1283 + */
1284 + private function maybe_optimize_postmeta() {
1285 + global $wpdb;
1286 +
1287 + /**
1288 + * Determines whether tables should be optimized.
1289 + *
1290 + * @param int Random number between 1 and 5000.
1291 + */
1292 + $optimize = (int) apply_filters( '_bbp_akismet_optimize_table', mt_rand( 1, 5000 ), $wpdb->postmeta );
1293 +
1294 + // Lucky number 11
1295 + if ( 11 === $optimize ) {
1296 + $wpdb->query( "OPTIMIZE TABLE {$wpdb->postmeta}" );
683 1297 }
684 1298 }
685 1299 }
686 1300 endif;