~canonical-sysadmins/wordpress/4.7.2

« back to all changes in this revision

Viewing changes to wp-content/plugins/akismet/class.akismet.php

  • Committer: Jacek Nykis
  • Date: 2015-01-05 16:17:05 UTC
  • Revision ID: jacek.nykis@canonical.com-20150105161705-w544l1h5mcg7u4w9
Initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
 
 
3
class Akismet {
 
4
        const API_HOST = 'rest.akismet.com';
 
5
        const API_PORT = 80;
 
6
        const MAX_DELAY_BEFORE_MODERATION_EMAIL = 86400; // One day in seconds
 
7
 
 
8
        private static $last_comment = '';
 
9
        private static $initiated = false;
 
10
        private static $prevent_moderation_email_for_these_comments = array();
 
11
        private static $last_comment_result = null;
 
12
        
 
13
        public static function init() {
 
14
                if ( ! self::$initiated ) {
 
15
                        self::init_hooks();
 
16
                }
 
17
        }
 
18
 
 
19
        /**
 
20
         * Initializes WordPress hooks
 
21
         */
 
22
        private static function init_hooks() {
 
23
                self::$initiated = true;
 
24
 
 
25
                add_action( 'wp_insert_comment', array( 'Akismet', 'auto_check_update_meta' ), 10, 2 );
 
26
                add_action( 'preprocess_comment', array( 'Akismet', 'auto_check_comment' ), 1 );
 
27
                add_action( 'akismet_scheduled_delete', array( 'Akismet', 'delete_old_comments' ) );
 
28
                add_action( 'akismet_scheduled_delete', array( 'Akismet', 'delete_old_comments_meta' ) );
 
29
                add_action( 'akismet_schedule_cron_recheck', array( 'Akismet', 'cron_recheck' ) );
 
30
 
 
31
                $akismet_comment_nonce_option = apply_filters( 'akismet_comment_nonce', get_option( 'akismet_comment_nonce' ) );
 
32
 
 
33
                if ( $akismet_comment_nonce_option == 'true' || $akismet_comment_nonce_option == '' )
 
34
                        add_action( 'comment_form',  array( 'Akismet',  'add_comment_nonce' ), 1 );
 
35
 
 
36
                add_action( 'admin_head-edit-comments.php', array( 'Akismet', 'load_form_js' ) );
 
37
                add_action( 'comment_form', array( 'Akismet', 'load_form_js' ) );
 
38
                add_action( 'comment_form', array( 'Akismet', 'inject_ak_js' ) );
 
39
 
 
40
                add_filter( 'comment_moderation_recipients', array( 'Akismet', 'disable_moderation_emails_if_unreachable' ), 1000, 2 );
 
41
                add_filter( 'pre_comment_approved', array( 'Akismet', 'last_comment_status' ), 10, 2 );
 
42
                
 
43
                add_action( 'transition_comment_status', array( 'Akismet', 'transition_comment_status' ), 10, 3 );
 
44
 
 
45
                if ( '3.0.5' == $GLOBALS['wp_version'] ) {
 
46
                        remove_filter( 'comment_text', 'wp_kses_data' );
 
47
                        if ( is_admin() )
 
48
                                add_filter( 'comment_text', 'wp_kses_post' );
 
49
                }
 
50
        }
 
51
 
 
52
        public static function get_api_key() {
 
53
                return apply_filters( 'akismet_get_api_key', defined('WPCOM_API_KEY') ? constant('WPCOM_API_KEY') : get_option('wordpress_api_key') );
 
54
        }
 
55
 
 
56
        public static function check_key_status( $key, $ip = null ) {
 
57
                return self::http_post( Akismet::build_query( array( 'key' => $key, 'blog' => get_option('home') ) ), 'verify-key', $ip );
 
58
        }
 
59
 
 
60
        public static function verify_key( $key, $ip = null ) {
 
61
                $response = self::check_key_status( $key, $ip );
 
62
 
 
63
                if ( $response[1] != 'valid' && $response[1] != 'invalid' )
 
64
                        return 'failed';
 
65
 
 
66
                self::update_alert( $response );
 
67
 
 
68
                return $response[1];
 
69
        }
 
70
 
 
71
        public static function auto_check_comment( $commentdata ) {
 
72
                self::$last_comment_result = null;
 
73
 
 
74
                $comment = $commentdata;
 
75
 
 
76
                $comment['user_ip']      = self::get_ip_address();
 
77
                $comment['user_agent']   = self::get_user_agent();
 
78
                $comment['referrer']     = self::get_referer();
 
79
                $comment['blog']         = get_option('home');
 
80
                $comment['blog_lang']    = get_locale();
 
81
                $comment['blog_charset'] = get_option('blog_charset');
 
82
                $comment['permalink']    = get_permalink( $comment['comment_post_ID'] );
 
83
 
 
84
                if ( !empty( $comment['user_ID'] ) )
 
85
                        $comment['user_role'] = Akismet::get_user_roles( $comment['user_ID'] );
 
86
 
 
87
                $akismet_nonce_option = apply_filters( 'akismet_comment_nonce', get_option( 'akismet_comment_nonce' ) );
 
88
                $comment['akismet_comment_nonce'] = 'inactive';
 
89
                if ( $akismet_nonce_option == 'true' || $akismet_nonce_option == '' ) {
 
90
                        $comment['akismet_comment_nonce'] = 'failed';
 
91
                        if ( isset( $_POST['akismet_comment_nonce'] ) && wp_verify_nonce( $_POST['akismet_comment_nonce'], 'akismet_comment_nonce_' . $comment['comment_post_ID'] ) )
 
92
                                $comment['akismet_comment_nonce'] = 'passed';
 
93
 
 
94
                        // comment reply in wp-admin
 
95
                        if ( isset( $_POST['_ajax_nonce-replyto-comment'] ) && check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' ) )
 
96
                                $comment['akismet_comment_nonce'] = 'passed';
 
97
 
 
98
                }
 
99
 
 
100
                if ( self::is_test_mode() )
 
101
                        $comment['is_test'] = 'true';
 
102
 
 
103
                foreach( $_POST as $key => $value ) {
 
104
                        if ( is_string( $value ) )
 
105
                                $comment["POST_{$key}"] = $value;
 
106
                }
 
107
 
 
108
                $ignore = array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' );
 
109
 
 
110
                foreach ( $_SERVER as $key => $value ) {
 
111
                        if ( !in_array( $key, $ignore ) && is_string($value) )
 
112
                                $comment["$key"] = $value;
 
113
                        else
 
114
                                $comment["$key"] = '';
 
115
                }
 
116
 
 
117
                $post = get_post( $comment['comment_post_ID'] );
 
118
                $comment[ 'comment_post_modified_gmt' ] = $post->post_modified_gmt;
 
119
 
 
120
                $response = self::http_post( Akismet::build_query( $comment ), 'comment-check' );
 
121
 
 
122
                do_action( 'akismet_comment_check_response', $response );
 
123
 
 
124
                self::update_alert( $response );
 
125
 
 
126
                $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' => '' ) );
 
127
                $commentdata['akismet_result']       = $response[1];
 
128
 
 
129
                if ( isset( $response[0]['x-akismet-pro-tip'] ) )
 
130
                $commentdata['akismet_pro_tip'] = $response[0]['x-akismet-pro-tip'];
 
131
 
 
132
                if ( isset( $response[0]['x-akismet-error'] ) ) {
 
133
                        // An error occurred that we anticipated (like a suspended key) and want the user to act on.
 
134
                        // Send to moderation.
 
135
                        self::$last_comment_result = '0';
 
136
                }
 
137
                else if ( 'true' == $response[1] ) {
 
138
                        // akismet_spam_count will be incremented later by comment_is_spam()
 
139
                        self::$last_comment_result = 'spam';
 
140
 
 
141
                        $discard = ( isset( $commentdata['akismet_pro_tip'] ) && $commentdata['akismet_pro_tip'] === 'discard' && self::allow_discard() );
 
142
 
 
143
                        do_action( 'akismet_spam_caught', $discard );
 
144
 
 
145
                        if ( $discard ) {
 
146
                                // akismet_result_spam() won't be called so bump the counter here
 
147
                                if ( $incr = apply_filters('akismet_spam_count_incr', 1) )
 
148
                                        update_option( 'akismet_spam_count', get_option('akismet_spam_count') + $incr );
 
149
                                $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : get_permalink( $post );
 
150
                                wp_safe_redirect( esc_url_raw( $redirect_to ) );
 
151
                                die();
 
152
                        }
 
153
                }
 
154
                
 
155
                // if the response is neither true nor false, hold the comment for moderation and schedule a recheck
 
156
                if ( 'true' != $response[1] && 'false' != $response[1] ) {
 
157
                        if ( !current_user_can('moderate_comments') ) {
 
158
                                // Comment status should be moderated
 
159
                                self::$last_comment_result = '0';
 
160
                        }
 
161
                        if ( function_exists('wp_next_scheduled') && function_exists('wp_schedule_single_event') ) {
 
162
                                if ( !wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) {
 
163
                                        wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' );
 
164
                                }
 
165
                        }
 
166
 
 
167
                        self::$prevent_moderation_email_for_these_comments[] = $commentdata;
 
168
                }
 
169
 
 
170
                if ( function_exists('wp_next_scheduled') && function_exists('wp_schedule_event') ) {
 
171
                        // WP 2.1+: delete old comments daily
 
172
                        if ( !wp_next_scheduled( 'akismet_scheduled_delete' ) )
 
173
                                wp_schedule_event( time(), 'daily', 'akismet_scheduled_delete' );
 
174
                }
 
175
                elseif ( (mt_rand(1, 10) == 3) ) {
 
176
                        // WP 2.0: run this one time in ten
 
177
                        self::delete_old_comments();
 
178
                }
 
179
                
 
180
                self::set_last_comment( $commentdata );
 
181
                self::fix_scheduled_recheck();
 
182
 
 
183
                return self::$last_comment;
 
184
        }
 
185
        
 
186
        public static function get_last_comment() {
 
187
                return self::$last_comment;
 
188
        }
 
189
        
 
190
        public static function set_last_comment( $comment ) {
 
191
                if ( is_null( $comment ) ) {
 
192
                        self::$last_comment = null;
 
193
                }
 
194
                else {
 
195
                        // We filter it here so that it matches the filtered comment data that we'll have to compare against later.
 
196
                        // wp_filter_comment expects comment_author_IP
 
197
                        self::$last_comment = wp_filter_comment(
 
198
                                array_merge(
 
199
                                        array( 'comment_author_IP' => self::get_ip_address() ),
 
200
                                        $comment
 
201
                                )
 
202
                        );
 
203
                }
 
204
        }
 
205
 
 
206
        // this fires on wp_insert_comment.  we can't update comment_meta when auto_check_comment() runs
 
207
        // because we don't know the comment ID at that point.
 
208
        public static function auto_check_update_meta( $id, $comment ) {
 
209
 
 
210
                // failsafe for old WP versions
 
211
                if ( !function_exists('add_comment_meta') )
 
212
                        return false;
 
213
 
 
214
                if ( !isset( self::$last_comment['comment_author_email'] ) )
 
215
                        self::$last_comment['comment_author_email'] = '';
 
216
 
 
217
                // wp_insert_comment() might be called in other contexts, so make sure this is the same comment
 
218
                // as was checked by auto_check_comment
 
219
                if ( is_object( $comment ) && !empty( self::$last_comment ) && is_array( self::$last_comment ) ) {
 
220
                        if ( self::matches_last_comment( $comment ) ) {
 
221
                                        
 
222
                                        load_plugin_textdomain( 'akismet' );
 
223
                                        
 
224
                                        // normal result: true or false
 
225
                                        if ( self::$last_comment['akismet_result'] == 'true' ) {
 
226
                                                update_comment_meta( $comment->comment_ID, 'akismet_result', 'true' );
 
227
                                                self::update_comment_history( $comment->comment_ID, __('Akismet caught this comment as spam', 'akismet'), 'check-spam' );
 
228
                                                if ( $comment->comment_approved != 'spam' )
 
229
                                                        self::update_comment_history( $comment->comment_ID, sprintf( __('Comment status was changed to %s', 'akismet'), $comment->comment_approved), 'status-changed'.$comment->comment_approved );
 
230
                                        }
 
231
                                        elseif ( self::$last_comment['akismet_result'] == 'false' ) {
 
232
                                                update_comment_meta( $comment->comment_ID, 'akismet_result', 'false' );
 
233
                                                self::update_comment_history( $comment->comment_ID, __('Akismet cleared this comment', 'akismet'), 'check-ham' );
 
234
                                                if ( $comment->comment_approved == 'spam' ) {
 
235
                                                        if ( wp_blacklist_check($comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent) )
 
236
                                                                self::update_comment_history( $comment->comment_ID, __('Comment was caught by wp_blacklist_check', 'akismet'), 'wp-blacklisted' );
 
237
                                                        else
 
238
                                                                self::update_comment_history( $comment->comment_ID, sprintf( __('Comment status was changed to %s', 'akismet'), $comment->comment_approved), 'status-changed-'.$comment->comment_approved );
 
239
                                                }
 
240
                                        } // abnormal result: error
 
241
                                        else {
 
242
                                                update_comment_meta( $comment->comment_ID, 'akismet_error', time() );
 
243
                                                self::update_comment_history( $comment->comment_ID, sprintf( __('Akismet was unable to check this comment (response: %s), will automatically retry again later.', 'akismet'), substr(self::$last_comment['akismet_result'], 0, 50)), 'check-error' );
 
244
                                        }
 
245
 
 
246
                                        // record the complete original data as submitted for checking
 
247
                                        if ( isset( self::$last_comment['comment_as_submitted'] ) )
 
248
                                                update_comment_meta( $comment->comment_ID, 'akismet_as_submitted', self::$last_comment['comment_as_submitted'] );
 
249
 
 
250
                                        if ( isset( self::$last_comment['akismet_pro_tip'] ) )
 
251
                                                update_comment_meta( $comment->comment_ID, 'akismet_pro_tip', self::$last_comment['akismet_pro_tip'] );
 
252
                        }
 
253
                }
 
254
        }
 
255
 
 
256
        public static function delete_old_comments() {
 
257
                global $wpdb;
 
258
 
 
259
                while( $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_id FROM {$wpdb->comments} WHERE DATE_SUB(NOW(), INTERVAL 15 DAY) > comment_date_gmt AND comment_approved = 'spam' LIMIT %d", defined( 'AKISMET_DELETE_LIMIT' ) ? AKISMET_DELETE_LIMIT : 10000 ) ) ) {
 
260
                        if ( empty( $comment_ids ) )
 
261
                                return;
 
262
 
 
263
                        $wpdb->queries = array();
 
264
 
 
265
                        do_action( 'delete_comment', $comment_ids );
 
266
 
 
267
                        $comma_comment_ids = implode( ', ', array_map('intval', $comment_ids) );
 
268
 
 
269
                        $wpdb->query("DELETE FROM {$wpdb->comments} WHERE comment_id IN ( $comma_comment_ids )");
 
270
                        $wpdb->query("DELETE FROM {$wpdb->commentmeta} WHERE comment_id IN ( $comma_comment_ids )");
 
271
 
 
272
                        clean_comment_cache( $comment_ids );
 
273
                }
 
274
 
 
275
                if ( apply_filters( 'akismet_optimize_table', ( mt_rand(1, 5000) == 11), $wpdb->comments ) ) // lucky number
 
276
                        $wpdb->query("OPTIMIZE TABLE {$wpdb->comments}");
 
277
        }
 
278
 
 
279
        public static function delete_old_comments_meta() {
 
280
                global $wpdb;
 
281
 
 
282
                $interval = apply_filters( 'akismet_delete_commentmeta_interval', 15 );
 
283
 
 
284
                # enfore a minimum of 1 day
 
285
                $interval = absint( $interval );
 
286
                if ( $interval < 1 )
 
287
                        $interval = 1;
 
288
 
 
289
                // akismet_as_submitted meta values are large, so expire them
 
290
                // after $interval days regardless of the comment status
 
291
                while ( $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT m.comment_id FROM {$wpdb->commentmeta} as m INNER JOIN {$wpdb->comments} as c USING(comment_id) WHERE m.meta_key = 'akismet_as_submitted' AND DATE_SUB(NOW(), INTERVAL %d DAY) > c.comment_date_gmt LIMIT 10000", $interval ) ) ) {
 
292
                        if ( empty( $comment_ids ) )
 
293
                                return;
 
294
 
 
295
                        $wpdb->queries = array();
 
296
 
 
297
                        foreach ( $comment_ids as $comment_id ) {
 
298
                                delete_comment_meta( $comment_id, 'akismet_as_submitted' );
 
299
                        }
 
300
                }
 
301
 
 
302
                if ( apply_filters( 'akismet_optimize_table', ( mt_rand(1, 5000) == 11), $wpdb->commentmeta ) ) // lucky number
 
303
                        $wpdb->query("OPTIMIZE TABLE {$wpdb->commentmeta}");
 
304
        }
 
305
 
 
306
        // how many approved comments does this author have?
 
307
        public static function get_user_comments_approved( $user_id, $comment_author_email, $comment_author, $comment_author_url ) {
 
308
                global $wpdb;
 
309
 
 
310
                if ( !empty( $user_id ) )
 
311
                        return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->comments} WHERE user_id = %d AND comment_approved = 1", $user_id ) );
 
312
 
 
313
                if ( !empty( $comment_author_email ) )
 
314
                        return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_author_email = %s AND comment_author = %s AND comment_author_url = %s AND comment_approved = 1", $comment_author_email, $comment_author, $comment_author_url ) );
 
315
 
 
316
                return 0;
 
317
        }
 
318
 
 
319
        // get the full comment history for a given comment, as an array in reverse chronological order
 
320
        public static function get_comment_history( $comment_id ) {
 
321
 
 
322
                // failsafe for old WP versions
 
323
                if ( !function_exists('add_comment_meta') )
 
324
                        return false;
 
325
 
 
326
                $history = get_comment_meta( $comment_id, 'akismet_history', false );
 
327
                usort( $history, array( 'Akismet', '_cmp_time' ) );
 
328
                return $history;
 
329
        }
 
330
 
 
331
        // log an event for a given comment, storing it in comment_meta
 
332
        public static function update_comment_history( $comment_id, $message, $event=null ) {
 
333
                global $current_user;
 
334
 
 
335
                // failsafe for old WP versions
 
336
                if ( !function_exists('add_comment_meta') )
 
337
                        return false;
 
338
 
 
339
                $user = '';
 
340
                if ( is_object( $current_user ) && isset( $current_user->user_login ) )
 
341
                        $user = $current_user->user_login;
 
342
 
 
343
                $event = array(
 
344
                        'time'    => self::_get_microtime(),
 
345
                        'message' => $message,
 
346
                        'event'   => $event,
 
347
                        'user'    => $user,
 
348
                );
 
349
 
 
350
                // $unique = false so as to allow multiple values per comment
 
351
                $r = add_comment_meta( $comment_id, 'akismet_history', $event, false );
 
352
        }
 
353
 
 
354
        public static function check_db_comment( $id, $recheck_reason = 'recheck_queue' ) {
 
355
                global $wpdb;
 
356
 
 
357
                $c = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $id ), ARRAY_A );
 
358
                if ( !$c )
 
359
                        return;
 
360
 
 
361
                $c['user_ip']        = $c['comment_author_IP'];
 
362
                $c['user_agent']     = $c['comment_agent'];
 
363
                $c['referrer']       = '';
 
364
                $c['blog']           = get_option('home');
 
365
                $c['blog_lang']      = get_locale();
 
366
                $c['blog_charset']   = get_option('blog_charset');
 
367
                $c['permalink']      = get_permalink($c['comment_post_ID']);
 
368
                $c['recheck_reason'] = $recheck_reason;
 
369
 
 
370
                if ( self::is_test_mode() )
 
371
                        $c['is_test'] = 'true';
 
372
 
 
373
                $response = self::http_post( Akismet::build_query( $c ), 'comment-check' );
 
374
 
 
375
                return ( is_array( $response ) && ! empty( $response[1] ) ) ? $response[1] : false;
 
376
        }
 
377
        
 
378
        
 
379
 
 
380
        public static function transition_comment_status( $new_status, $old_status, $comment ) {
 
381
                
 
382
                if ( $new_status == $old_status )
 
383
                        return;
 
384
 
 
385
                # we don't need to record a history item for deleted comments
 
386
                if ( $new_status == 'delete' )
 
387
                        return;
 
388
                
 
389
                if ( !current_user_can( 'edit_post', $comment->comment_post_ID ) && !current_user_can( 'moderate_comments' ) )
 
390
                        return;
 
391
 
 
392
                if ( defined('WP_IMPORTING') && WP_IMPORTING == true )
 
393
                        return;
 
394
                        
 
395
                // if this is present, it means the status has been changed by a re-check, not an explicit user action
 
396
                if ( get_comment_meta( $comment->comment_ID, 'akismet_rechecking' ) )
 
397
                        return;
 
398
                
 
399
                global $current_user;
 
400
                $reporter = '';
 
401
                if ( is_object( $current_user ) )
 
402
                        $reporter = $current_user->user_login;
 
403
 
 
404
                // Assumption alert:
 
405
                // We want to submit comments to Akismet only when a moderator explicitly spams or approves it - not if the status
 
406
                // is changed automatically by another plugin.  Unfortunately WordPress doesn't provide an unambiguous way to
 
407
                // determine why the transition_comment_status action was triggered.  And there are several different ways by which
 
408
                // to spam and unspam comments: bulk actions, ajax, links in moderation emails, the dashboard, and perhaps others.
 
409
                // We'll assume that this is an explicit user action if certain POST/GET variables exist.
 
410
                if ( ( isset( $_POST['status'] ) && in_array( $_POST['status'], array( 'spam', 'unspam' ) ) ) ||
 
411
                         ( isset( $_POST['spam'] )   && (int) $_POST['spam'] == 1 ) ||
 
412
                         ( isset( $_POST['unspam'] ) && (int) $_POST['unspam'] == 1 ) ||
 
413
                         ( isset( $_POST['comment_status'] )  && in_array( $_POST['comment_status'], array( 'spam', 'unspam' ) ) ) ||
 
414
                         ( isset( $_GET['action'] )  && in_array( $_GET['action'], array( 'spam', 'unspam' ) ) ) ||
 
415
                         ( isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'editedcomment' ) ) )
 
416
                 ) {
 
417
                        if ( $new_status == 'spam' && ( $old_status == 'approved' || $old_status == 'unapproved' || !$old_status ) ) {
 
418
                                return self::submit_spam_comment( $comment->comment_ID );
 
419
                        } elseif ( $old_status == 'spam' && ( $new_status == 'approved' || $new_status == 'unapproved' ) ) {
 
420
                                return self::submit_nonspam_comment( $comment->comment_ID );
 
421
                        }
 
422
                }
 
423
 
 
424
                self::update_comment_history( $comment->comment_ID, sprintf( __('%1$s changed the comment status to %2$s', 'akismet'), $reporter, $new_status ), 'status-' . $new_status );
 
425
        }
 
426
        
 
427
        public static function submit_spam_comment( $comment_id ) {
 
428
                global $wpdb, $current_user, $current_site;
 
429
 
 
430
                $comment_id = (int) $comment_id;
 
431
 
 
432
                $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) );
 
433
 
 
434
                if ( !$comment ) // it was deleted
 
435
                        return;
 
436
 
 
437
                if ( 'spam' != $comment->comment_approved )
 
438
                        return;
 
439
 
 
440
                // use the original version stored in comment_meta if available
 
441
                $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true);
 
442
 
 
443
                if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) )
 
444
                        $comment = (object) array_merge( (array)$comment, $as_submitted );
 
445
 
 
446
                $comment->blog         = get_bloginfo('url');
 
447
                $comment->blog_lang    = get_locale();
 
448
                $comment->blog_charset = get_option('blog_charset');
 
449
                $comment->permalink    = get_permalink($comment->comment_post_ID);
 
450
 
 
451
                if ( is_object($current_user) )
 
452
                        $comment->reporter = $current_user->user_login;
 
453
 
 
454
                if ( is_object($current_site) )
 
455
                        $comment->site_domain = $current_site->domain;
 
456
 
 
457
                $comment->user_role = '';
 
458
                if ( isset( $comment->user_ID ) )
 
459
                        $comment->user_role = Akismet::get_user_roles( $comment->user_ID );
 
460
 
 
461
                if ( self::is_test_mode() )
 
462
                        $comment->is_test = 'true';
 
463
 
 
464
                $post = get_post( $comment->comment_post_ID );
 
465
                $comment->comment_post_modified_gmt = $post->post_modified_gmt;
 
466
 
 
467
                $response = Akismet::http_post( Akismet::build_query( $comment ), 'submit-spam' );
 
468
                if ( $comment->reporter ) {
 
469
                        self::update_comment_history( $comment_id, sprintf( __('%s reported this comment as spam', 'akismet'), $comment->reporter ), 'report-spam' );
 
470
                        update_comment_meta( $comment_id, 'akismet_user_result', 'true' );
 
471
                        update_comment_meta( $comment_id, 'akismet_user', $comment->reporter );
 
472
                }
 
473
 
 
474
                do_action('akismet_submit_spam_comment', $comment_id, $response[1]);
 
475
        }
 
476
 
 
477
        public static function submit_nonspam_comment( $comment_id ) {
 
478
                global $wpdb, $current_user, $current_site;
 
479
 
 
480
                $comment_id = (int) $comment_id;
 
481
 
 
482
                $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) );
 
483
                if ( !$comment ) // it was deleted
 
484
                        return;
 
485
 
 
486
                // use the original version stored in comment_meta if available
 
487
                $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true);
 
488
 
 
489
                if ( $as_submitted && is_array($as_submitted) && isset($as_submitted['comment_content']) )
 
490
                        $comment = (object) array_merge( (array)$comment, $as_submitted );
 
491
 
 
492
                $comment->blog         = get_bloginfo('url');
 
493
                $comment->blog_lang    = get_locale();
 
494
                $comment->blog_charset = get_option('blog_charset');
 
495
                $comment->permalink    = get_permalink( $comment->comment_post_ID );
 
496
                $comment->user_role    = '';
 
497
 
 
498
                if ( is_object($current_user) )
 
499
                        $comment->reporter = $current_user->user_login;
 
500
 
 
501
                if ( is_object($current_site) )
 
502
                        $comment->site_domain = $current_site->domain;
 
503
 
 
504
                if ( isset( $comment->user_ID ) )
 
505
                        $comment->user_role = Akismet::get_user_roles($comment->user_ID);
 
506
 
 
507
                if ( Akismet::is_test_mode() )
 
508
                        $comment->is_test = 'true';
 
509
 
 
510
                $post = get_post( $comment->comment_post_ID );
 
511
                $comment->comment_post_modified_gmt = $post->post_modified_gmt;
 
512
 
 
513
                $response = self::http_post( Akismet::build_query( $comment ), 'submit-ham' );
 
514
                if ( $comment->reporter ) {
 
515
                        self::update_comment_history( $comment_id, sprintf( __('%s reported this comment as not spam', 'akismet'), $comment->reporter ), 'report-ham' );
 
516
                        update_comment_meta( $comment_id, 'akismet_user_result', 'false' );
 
517
                        update_comment_meta( $comment_id, 'akismet_user', $comment->reporter );
 
518
                }
 
519
 
 
520
                do_action('akismet_submit_nonspam_comment', $comment_id, $response[1]);
 
521
        }
 
522
 
 
523
        public static function cron_recheck() {
 
524
                global $wpdb;
 
525
 
 
526
                $api_key = self::get_api_key();
 
527
 
 
528
                $status = self::verify_key( $api_key );
 
529
                if ( get_option( 'akismet_alert_code' ) || $status == 'invalid' ) {
 
530
                        // since there is currently a problem with the key, reschedule a check for 6 hours hence
 
531
                        wp_schedule_single_event( time() + 21600, 'akismet_schedule_cron_recheck' );
 
532
                        return false;
 
533
                }
 
534
 
 
535
                delete_option('akismet_available_servers');
 
536
 
 
537
                $comment_errors = $wpdb->get_col( "SELECT comment_id FROM {$wpdb->commentmeta} WHERE meta_key = 'akismet_error' LIMIT 100" );
 
538
                
 
539
                load_plugin_textdomain( 'akismet' );
 
540
 
 
541
                foreach ( (array) $comment_errors as $comment_id ) {
 
542
                        // if the comment no longer exists, or is too old, remove the meta entry from the queue to avoid getting stuck
 
543
                        $comment = get_comment( $comment_id );
 
544
                        if ( !$comment || strtotime( $comment->comment_date_gmt ) < strtotime( "-15 days" ) ) {
 
545
                                delete_comment_meta( $comment_id, 'akismet_error' );
 
546
                                delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' );
 
547
                                continue;
 
548
                        }
 
549
 
 
550
                        add_comment_meta( $comment_id, 'akismet_rechecking', true );
 
551
                        $status = self::check_db_comment( $comment_id, 'retry' );
 
552
 
 
553
                        $msg = '';
 
554
                        if ( $status == 'true' ) {
 
555
                                $msg = __( 'Akismet caught this comment as spam during an automatic retry.' , 'akismet');
 
556
                        } elseif ( $status == 'false' ) {
 
557
                                $msg = __( 'Akismet cleared this comment during an automatic retry.' , 'akismet');
 
558
                        }
 
559
 
 
560
                        // If we got back a legit response then update the comment history
 
561
                        // other wise just bail now and try again later.  No point in
 
562
                        // re-trying all the comments once we hit one failure.
 
563
                        if ( !empty( $msg ) ) {
 
564
                                delete_comment_meta( $comment_id, 'akismet_error' );
 
565
                                self::update_comment_history( $comment_id, $msg, 'cron-retry' );
 
566
                                update_comment_meta( $comment_id, 'akismet_result', $status );
 
567
                                // make sure the comment status is still pending.  if it isn't, that means the user has already moved it elsewhere.
 
568
                                $comment = get_comment( $comment_id );
 
569
                                if ( $comment && 'unapproved' == wp_get_comment_status( $comment_id ) ) {
 
570
                                        if ( $status == 'true' ) {
 
571
                                                wp_spam_comment( $comment_id );
 
572
                                        } elseif ( $status == 'false' ) {
 
573
                                                // comment is good, but it's still in the pending queue.  depending on the moderation settings
 
574
                                                // we may need to change it to approved.
 
575
                                                if ( check_comment($comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent, $comment->comment_type) )
 
576
                                                        wp_set_comment_status( $comment_id, 1 );
 
577
                                                else if ( get_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true ) )
 
578
                                                        wp_notify_moderator( $comment_id );
 
579
                                        }
 
580
                                }
 
581
                                
 
582
                                delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' );
 
583
                        } else {
 
584
                                // If this comment has been pending moderation for longer than MAX_DELAY_BEFORE_MODERATION_EMAIL,
 
585
                                // send a moderation email now.
 
586
                                if ( ( intval( gmdate( 'U' ) ) - strtotime( $comment->comment_date_gmt ) ) < self::MAX_DELAY_BEFORE_MODERATION_EMAIL ) {
 
587
                                        delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' );
 
588
                                        wp_notify_moderator( $comment_id );
 
589
                                }
 
590
 
 
591
                                delete_comment_meta( $comment_id, 'akismet_rechecking' );
 
592
                                wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' );
 
593
                                return;
 
594
                        }
 
595
                        delete_comment_meta( $comment_id, 'akismet_rechecking' );
 
596
                }
 
597
 
 
598
                $remaining = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->commentmeta} WHERE meta_key = 'akismet_error'" );
 
599
                if ( $remaining && !wp_next_scheduled('akismet_schedule_cron_recheck') ) {
 
600
                        wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' );
 
601
                }
 
602
        }
 
603
 
 
604
        public static function fix_scheduled_recheck() {
 
605
                $future_check = wp_next_scheduled( 'akismet_schedule_cron_recheck' );
 
606
                if ( !$future_check ) {
 
607
                        return;
 
608
                }
 
609
 
 
610
                if ( get_option( 'akismet_alert_code' ) > 0 ) {
 
611
                        return;
 
612
                }
 
613
 
 
614
                $check_range = time() + 1200;
 
615
                if ( $future_check > $check_range ) {
 
616
                        wp_clear_scheduled_hook( 'akismet_schedule_cron_recheck' );
 
617
                        wp_schedule_single_event( time() + 300, 'akismet_schedule_cron_recheck' );
 
618
                }
 
619
        }
 
620
 
 
621
        public static function add_comment_nonce( $post_id ) {
 
622
                echo '<p style="display: none;">';
 
623
                wp_nonce_field( 'akismet_comment_nonce_' . $post_id, 'akismet_comment_nonce', FALSE );
 
624
                echo '</p>';
 
625
        }
 
626
 
 
627
        public static function is_test_mode() {
 
628
                return defined('AKISMET_TEST_MODE') && AKISMET_TEST_MODE;
 
629
        }
 
630
        
 
631
        public static function allow_discard() {
 
632
                if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
 
633
                        return false;
 
634
                if ( is_user_logged_in() )
 
635
                        return false;
 
636
        
 
637
                return ( get_option( 'akismet_strictness' ) === '1'  );
 
638
        }
 
639
 
 
640
        public static function get_ip_address() {
 
641
                return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : null;
 
642
        }
 
643
        
 
644
        /**
 
645
         * Do these two comments, without checking the comment_ID, "match"?
 
646
         *
 
647
         * @param mixed $comment1 A comment object or array.
 
648
         * @param mixed $comment2 A comment object or array.
 
649
         * @return bool Whether the two comments should be treated as the same comment.
 
650
         */
 
651
        private static function comments_match( $comment1, $comment2 ) {
 
652
                $comment1 = (array) $comment1;
 
653
                $comment2 = (array) $comment2;
 
654
                
 
655
                return (
 
656
                           isset( $comment1['comment_post_ID'], $comment2['comment_post_ID'] )
 
657
                        && intval( $comment1['comment_post_ID'] ) == intval( $comment2['comment_post_ID'] )
 
658
                        && $comment1['comment_author'] == $comment2['comment_author']
 
659
                        && $comment1['comment_author_email'] == $comment2['comment_author_email']
 
660
                );
 
661
        }
 
662
        
 
663
        // Does the supplied comment match the details of the one most recently stored in self::$last_comment?
 
664
        public static function matches_last_comment( $comment ) {
 
665
                if ( is_object( $comment ) )
 
666
                        $comment = (array) $comment;
 
667
 
 
668
                return self::comments_match( self::$last_comment, $comment );
 
669
        }
 
670
 
 
671
        private static function get_user_agent() {
 
672
                return isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null;
 
673
        }
 
674
 
 
675
        private static function get_referer() {
 
676
                return isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : null;
 
677
        }
 
678
 
 
679
        // return a comma-separated list of role names for the given user
 
680
        public static function get_user_roles( $user_id ) {
 
681
                $roles = false;
 
682
 
 
683
                if ( !class_exists('WP_User') )
 
684
                        return false;
 
685
 
 
686
                if ( $user_id > 0 ) {
 
687
                        $comment_user = new WP_User( $user_id );
 
688
                        if ( isset( $comment_user->roles ) )
 
689
                                $roles = join( ',', $comment_user->roles );
 
690
                }
 
691
 
 
692
                if ( is_multisite() && is_super_admin( $user_id ) ) {
 
693
                        if ( empty( $roles ) ) {
 
694
                                $roles = 'super_admin';
 
695
                        } else {
 
696
                                $comment_user->roles[] = 'super_admin';
 
697
                                $roles = join( ',', $comment_user->roles );
 
698
                        }
 
699
                }
 
700
 
 
701
                return $roles;
 
702
        }
 
703
 
 
704
        // filter handler used to return a spam result to pre_comment_approved
 
705
        public static function last_comment_status( $approved, $comment ) {
 
706
                // Only do this if it's the correct comment
 
707
                if ( is_null(self::$last_comment_result) || ! self::matches_last_comment( $comment ) ) {
 
708
                        self::log( "comment_is_spam mismatched comment, returning unaltered $approved" );
 
709
                        return $approved;
 
710
                }
 
711
 
 
712
                // bump the counter here instead of when the filter is added to reduce the possibility of overcounting
 
713
                if ( $incr = apply_filters('akismet_spam_count_incr', 1) )
 
714
                        update_option( 'akismet_spam_count', get_option('akismet_spam_count') + $incr );
 
715
 
 
716
                return self::$last_comment_result;
 
717
        }
 
718
        
 
719
        /**
 
720
         * If Akismet is temporarily unreachable, we don't want to "spam" the blogger with
 
721
         * moderation emails for comments that will be automatically cleared or spammed on
 
722
         * the next retry.
 
723
         *
 
724
         * For comments that will be rechecked later, empty the list of email addresses that
 
725
         * the moderation email would be sent to.
 
726
         *
 
727
         * @param array $emails An array of email addresses that the moderation email will be sent to.
 
728
         * @param int $comment_id The ID of the relevant comment.
 
729
         * @return array An array of email addresses that the moderation email will be sent to.
 
730
         */
 
731
        public static function disable_moderation_emails_if_unreachable( $emails, $comment_id ) {
 
732
                if ( ! empty( self::$prevent_moderation_email_for_these_comments ) && ! empty( $emails ) ) {
 
733
                        $comment = get_comment( $comment_id );
 
734
 
 
735
                        foreach ( self::$prevent_moderation_email_for_these_comments as $possible_match ) {
 
736
                                if ( self::comments_match( $possible_match, $comment ) ) {
 
737
                                        update_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true );
 
738
                                        return array();
 
739
                                }
 
740
                        }
 
741
                }
 
742
 
 
743
                return $emails;
 
744
        }
 
745
 
 
746
        public static function _cmp_time( $a, $b ) {
 
747
                return $a['time'] > $b['time'] ? -1 : 1;
 
748
        }
 
749
 
 
750
        public static function _get_microtime() {
 
751
                $mtime = explode( ' ', microtime() );
 
752
                return $mtime[1] + $mtime[0];
 
753
        }
 
754
 
 
755
        /**
 
756
         * Make a POST request to the Akismet API.
 
757
         *
 
758
         * @param string $request The body of the request.
 
759
         * @param string $path The path for the request.
 
760
         * @param string $ip The specific IP address to hit.
 
761
         * @return array A two-member array consisting of the headers and the response body, both empty in the case of a failure.
 
762
         */
 
763
        public static function http_post( $request, $path, $ip=null ) {
 
764
 
 
765
                $akismet_ua = sprintf( 'WordPress/%s | Akismet/%s', $GLOBALS['wp_version'], constant( 'AKISMET_VERSION' ) );
 
766
                $akismet_ua = apply_filters( 'akismet_ua', $akismet_ua );
 
767
 
 
768
                $content_length = strlen( $request );
 
769
 
 
770
                $api_key   = self::get_api_key();
 
771
                $host      = self::API_HOST;
 
772
 
 
773
                if ( !empty( $api_key ) )
 
774
                        $host = $api_key.'.'.$host;
 
775
 
 
776
                $http_host = $host;
 
777
                // use a specific IP if provided
 
778
                // needed by Akismet_Admin::check_server_connectivity()
 
779
                if ( $ip && long2ip( ip2long( $ip ) ) ) {
 
780
                        $http_host = $ip;
 
781
                }
 
782
 
 
783
                $http_args = array(
 
784
                        'body' => $request,
 
785
                        'headers' => array(
 
786
                                'Content-Type' => 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ),
 
787
                                'Host' => $host,
 
788
                                'User-Agent' => $akismet_ua,
 
789
                        ),
 
790
                        'httpversion' => '1.0',
 
791
                        'timeout' => 15
 
792
                );
 
793
 
 
794
                $akismet_url = "http://{$http_host}/1.1/{$path}";
 
795
                $response = wp_remote_post( $akismet_url, $http_args );
 
796
                Akismet::log( compact( 'akismet_url', 'http_args', 'response' ) );
 
797
                if ( is_wp_error( $response ) )
 
798
                        return array( '', '' );
 
799
 
 
800
                return array( $response['headers'], $response['body'] );
 
801
        }
 
802
 
 
803
        // given a response from an API call like check_key_status(), update the alert code options if an alert is present.
 
804
        private static function update_alert( $response ) {
 
805
                $code = $msg = null;
 
806
                if ( isset( $response[0]['x-akismet-alert-code'] ) ) {
 
807
                        $code = $response[0]['x-akismet-alert-code'];
 
808
                        $msg  = $response[0]['x-akismet-alert-msg'];
 
809
                }
 
810
 
 
811
                // only call update_option() if the value has changed
 
812
                if ( $code != get_option( 'akismet_alert_code' ) ) {
 
813
                        if ( ! $code ) {
 
814
                                delete_option( 'akismet_alert_code' );
 
815
                                delete_option( 'akismet_alert_msg' );
 
816
                        }
 
817
                        else {
 
818
                                update_option( 'akismet_alert_code', $code );
 
819
                                update_option( 'akismet_alert_msg', $msg );
 
820
                        }
 
821
                }
 
822
        }
 
823
 
 
824
        public static function load_form_js() {
 
825
                // WP < 3.3 can't enqueue a script this late in the game and still have it appear in the footer.
 
826
                // Once we drop support for everything pre-3.3, this can change back to a single enqueue call.
 
827
                wp_register_script( 'akismet-form', AKISMET__PLUGIN_URL . '_inc/form.js', array(), AKISMET_VERSION, true );
 
828
                add_action( 'wp_footer', array( 'Akismet', 'print_form_js' ) );
 
829
                add_action( 'admin_footer', array( 'Akismet', 'print_form_js' ) );
 
830
        }
 
831
        
 
832
        public static function print_form_js() {
 
833
                wp_print_scripts( 'akismet-form' );
 
834
        }
 
835
 
 
836
        public static function inject_ak_js( $fields ) {
 
837
                echo '<p style="display: none;">';
 
838
                echo '<input type="hidden" id="ak_js" name="ak_js" value="' . mt_rand( 0, 250 ) . '"/>';
 
839
                echo '</p>';
 
840
        }
 
841
 
 
842
        private static function bail_on_activation( $message, $deactivate = true ) {
 
843
?>
 
844
<!doctype html>
 
845
<html>
 
846
<head>
 
847
<meta charset="<?php bloginfo( 'charset' ); ?>">
 
848
<style>
 
849
* {
 
850
        text-align: center;
 
851
        margin: 0;
 
852
        padding: 0;
 
853
        font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
 
854
}
 
855
p {
 
856
        margin-top: 1em;
 
857
        font-size: 18px;
 
858
}
 
859
</style>
 
860
<body>
 
861
<p><?php echo esc_html( $message ); ?></p>
 
862
</body>
 
863
</html>
 
864
<?php
 
865
                if ( $deactivate ) {
 
866
                        $plugins = get_option( 'active_plugins' );
 
867
                        $akismet = plugin_basename( AKISMET__PLUGIN_DIR . 'akismet.php' );
 
868
                        $update  = false;
 
869
                        foreach ( $plugins as $i => $plugin ) {
 
870
                                if ( $plugin === $akismet ) {
 
871
                                        $plugins[$i] = false;
 
872
                                        $update = true;
 
873
                                }
 
874
                        }
 
875
 
 
876
                        if ( $update ) {
 
877
                                update_option( 'active_plugins', array_filter( $plugins ) );
 
878
                        }
 
879
                }
 
880
                exit;
 
881
        }
 
882
 
 
883
        public static function view( $name, array $args = array() ) {
 
884
                $args = apply_filters( 'akismet_view_arguments', $args, $name );
 
885
                
 
886
                foreach ( $args AS $key => $val ) {
 
887
                        $$key = $val;
 
888
                }
 
889
                
 
890
                load_plugin_textdomain( 'akismet' );
 
891
 
 
892
                $file = AKISMET__PLUGIN_DIR . 'views/'. $name . '.php';
 
893
 
 
894
                include( $file );
 
895
        }
 
896
 
 
897
        /**
 
898
         * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
 
899
         * @static
 
900
         */
 
901
        public static function plugin_activation() {
 
902
                if ( version_compare( $GLOBALS['wp_version'], AKISMET__MINIMUM_WP_VERSION, '<' ) ) {
 
903
                        load_plugin_textdomain( 'akismet' );
 
904
                        
 
905
                        $message = '<strong>'.sprintf(esc_html__( 'Akismet %s requires WordPress %s or higher.' , 'akismet'), AKISMET_VERSION, AKISMET__MINIMUM_WP_VERSION ).'</strong> '.sprintf(__('Please <a href="%1$s">upgrade WordPress</a> to a current version, or <a href="%2$s">downgrade to version 2.4 of the Akismet plugin</a>.', 'akismet'), 'https://codex.wordpress.org/Upgrading_WordPress', 'http://wordpress.org/extend/plugins/akismet/download/');
 
906
 
 
907
                        Akismet::bail_on_activation( $message );
 
908
                }
 
909
        }
 
910
 
 
911
        /**
 
912
         * Removes all connection options
 
913
         * @static
 
914
         */
 
915
        public static function plugin_deactivation( ) {
 
916
                //tidy up
 
917
        }
 
918
        
 
919
        /**
 
920
         * Essentially a copy of WP's build_query but one that doesn't expect pre-urlencoded values.
 
921
         *
 
922
         * @param array $args An array of key => value pairs
 
923
         * @return string A string ready for use as a URL query string.
 
924
         */
 
925
        public static function build_query( $args ) {
 
926
                return _http_build_query( $args, '', '&' );
 
927
        }
 
928
 
 
929
        public static function log( $akismet_debug ) {
 
930
                if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG )
 
931
                        error_log( print_r( compact( 'akismet_debug' ), 1 ) ); //send message to debug.log when in debug mode
 
932
        }
 
933
}
 
 
b'\\ No newline at end of file'