3066 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			3066 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | ||
| /**
 | ||
|  * Plugin Name: Antispam Bee
 | ||
|  * Description: Antispam plugin with a sophisticated toolset for effective day to day comment and trackback spam-fighting. Built with data protection and privacy in mind.
 | ||
|  * Author:      pluginkollektiv
 | ||
|  * Author URI:  https://pluginkollektiv.org
 | ||
|  * Plugin URI:  https://antispambee.pluginkollektiv.org/
 | ||
|  * Text Domain: antispam-bee
 | ||
|  * Domain Path: /lang
 | ||
|  * License:     GPLv2 or later
 | ||
|  * License URI: http://www.gnu.org/licenses/gpl-2.0.html
 | ||
|  * Version:     2.11.5
 | ||
|  *
 | ||
|  * @package Antispam Bee
 | ||
|  **/
 | ||
| 
 | ||
| /*
 | ||
| * Copyright (C)  2009-2015 Sergej Müller
 | ||
| *
 | ||
| * This program is free software; you can redistribute it and/or modify
 | ||
| * it under the terms of the GNU General Public License as published by
 | ||
| * the Free Software Foundation; either version 2 of the License, or
 | ||
| * (at your option) any later version.
 | ||
| *
 | ||
| * This program is distributed in the hope that it will be useful,
 | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||
| * GNU General Public License for more details.
 | ||
| *
 | ||
| * You should have received a copy of the GNU General Public License along
 | ||
| * with this program; if not, write to the Free Software Foundation, Inc.,
 | ||
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | ||
| */
 | ||
| 
 | ||
| 
 | ||
| // Make sure this file is only run from within the WordPress context.
 | ||
| defined( 'ABSPATH' ) || exit;
 | ||
| 
 | ||
| /**
 | ||
|  * Antispam_Bee
 | ||
|  *
 | ||
|  * @since  0.1
 | ||
|  * @since  2.4
 | ||
|  */
 | ||
| class Antispam_Bee {
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * The option defaults.
 | ||
| 	 *
 | ||
| 	 * @var array
 | ||
| 	 */
 | ||
| 	public static $defaults;
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Which internal datastructure version we are running on.
 | ||
| 	 *
 | ||
| 	 * @var int
 | ||
| 	 */
 | ||
| 	private static $db_version = 1.02;
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * The base.
 | ||
| 	 *
 | ||
| 	 * @var string
 | ||
| 	 */
 | ||
| 	private static $_base;
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * The salt.
 | ||
| 	 *
 | ||
| 	 * @var string
 | ||
| 	 */
 | ||
| 	private static $_salt;
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * The spam reason.
 | ||
| 	 *
 | ||
| 	 * @var string
 | ||
| 	 */
 | ||
| 	private static $_reason;
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * The current Post ID.
 | ||
| 	 *
 | ||
| 	 * @var int
 | ||
| 	 */
 | ||
| 	private static $_current_post_id;
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * "Constructor" of the class
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.6.4
 | ||
| 	 * @since  2.10.0 Change handling of comment field honeypot and call functions after completed upgrades
 | ||
| 	 * @since  2.11.0 Change priority from default 10 to 99 for comment_form_field_comment
 | ||
| 	 */
 | ||
| 	public static function init() {
 | ||
| 		add_action(
 | ||
| 			'upgrader_process_complete',
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'upgrades_completed',
 | ||
| 			),
 | ||
| 			10,
 | ||
| 			2
 | ||
| 		);
 | ||
| 
 | ||
| 		add_action(
 | ||
| 			'upgrader_overwrote_package',
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'uploaded_upgrade_completed',
 | ||
| 			),
 | ||
| 			10,
 | ||
| 			3
 | ||
| 		);
 | ||
| 
 | ||
| 		add_action(
 | ||
| 			'unspam_comment',
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'delete_spam_reason_by_comment',
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		add_action(
 | ||
| 			'comment_unapproved_to_spam',
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'update_antispam_bee_reason',
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		add_action(
 | ||
| 			'comment_approved_to_spam',
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'update_antispam_bee_reason',
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		$disallow_ajax = apply_filters( 'antispam_bee_disallow_ajax_calls', true );
 | ||
| 
 | ||
| 		if ( defined( 'DOING_AJAX' ) && DOING_AJAX && $disallow_ajax ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		self::_init_internal_vars();
 | ||
| 
 | ||
| 		if ( self::_current_page( 'dashboard' ) || self::_current_page( 'plugins' ) || self::_current_page( 'options' ) || self::_current_page( 'edit-comments' ) || self::_current_page( 'admin-post' ) ) {
 | ||
| 			self::load_plugin_lang();
 | ||
| 			self::add_reasons_to_defaults();
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( defined( 'DOING_CRON' ) ) {
 | ||
| 			add_action(
 | ||
| 				'antispam_bee_daily_cronjob',
 | ||
| 				array(
 | ||
| 					__CLASS__,
 | ||
| 					'start_daily_cronjob',
 | ||
| 				)
 | ||
| 			);
 | ||
| 
 | ||
| 		} elseif ( is_admin() ) {
 | ||
| 			add_action(
 | ||
| 				'admin_menu',
 | ||
| 				array(
 | ||
| 					__CLASS__,
 | ||
| 					'add_sidebar_menu',
 | ||
| 				)
 | ||
| 			);
 | ||
| 
 | ||
| 			if ( self::_current_page( 'dashboard' ) ) {
 | ||
| 				add_filter(
 | ||
| 					'dashboard_glance_items',
 | ||
| 					array(
 | ||
| 						__CLASS__,
 | ||
| 						'add_dashboard_count',
 | ||
| 					)
 | ||
| 				);
 | ||
| 				add_action(
 | ||
| 					'wp_dashboard_setup',
 | ||
| 					array(
 | ||
| 						__CLASS__,
 | ||
| 						'add_dashboard_chart',
 | ||
| 					)
 | ||
| 				);
 | ||
| 
 | ||
| 			} elseif ( self::_current_page( 'plugins' ) ) {
 | ||
| 				add_filter(
 | ||
| 					'plugin_row_meta',
 | ||
| 					array(
 | ||
| 						__CLASS__,
 | ||
| 						'init_row_meta',
 | ||
| 					),
 | ||
| 					10,
 | ||
| 					2
 | ||
| 				);
 | ||
| 				add_filter(
 | ||
| 					'plugin_action_links_' . self::$_base,
 | ||
| 					array(
 | ||
| 						__CLASS__,
 | ||
| 						'init_action_links',
 | ||
| 					)
 | ||
| 				);
 | ||
| 
 | ||
| 			} elseif ( self::_current_page( 'options' ) ) {
 | ||
| 				add_action(
 | ||
| 					'admin_init',
 | ||
| 					array(
 | ||
| 						__CLASS__,
 | ||
| 						'init_plugin_sources',
 | ||
| 					)
 | ||
| 				);
 | ||
| 				add_action(
 | ||
| 					'admin_init',
 | ||
| 					array(
 | ||
| 						__CLASS__,
 | ||
| 						'update_database',
 | ||
| 					)
 | ||
| 				);
 | ||
| 
 | ||
| 			} elseif ( self::_current_page( 'admin-post' ) ) {
 | ||
| 				require_once dirname( __FILE__ ) . '/inc/gui.class.php';
 | ||
| 
 | ||
| 				add_action(
 | ||
| 					'admin_post_ab_save_changes',
 | ||
| 					array(
 | ||
| 						'Antispam_Bee_GUI',
 | ||
| 						'save_changes',
 | ||
| 					)
 | ||
| 				);
 | ||
| 
 | ||
| 			} elseif ( self::_current_page( 'edit-comments' ) ) {
 | ||
| 				// phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
 | ||
| 				if ( ! empty( $_GET['comment_status'] ) && 'spam' === $_GET['comment_status'] && ! self::get_option( 'no_notice' ) ) {
 | ||
| 					// phpcs:enable WordPress.CSRF.NonceVerification.NoNonceVerification
 | ||
| 					require_once dirname( __FILE__ ) . '/inc/columns.class.php';
 | ||
| 
 | ||
| 					add_filter(
 | ||
| 						'manage_edit-comments_columns',
 | ||
| 						array(
 | ||
| 							'Antispam_Bee_Columns',
 | ||
| 							'register_plugin_columns',
 | ||
| 						)
 | ||
| 					);
 | ||
| 					add_filter(
 | ||
| 						'manage_comments_custom_column',
 | ||
| 						array(
 | ||
| 							'Antispam_Bee_Columns',
 | ||
| 							'print_plugin_column',
 | ||
| 						),
 | ||
| 						10,
 | ||
| 						2
 | ||
| 					);
 | ||
| 					add_filter(
 | ||
| 						'admin_print_styles-edit-comments.php',
 | ||
| 						array(
 | ||
| 							'Antispam_Bee_Columns',
 | ||
| 							'print_column_styles',
 | ||
| 						)
 | ||
| 					);
 | ||
| 
 | ||
| 					add_filter(
 | ||
| 						'manage_edit-comments_sortable_columns',
 | ||
| 						array(
 | ||
| 							'Antispam_Bee_Columns',
 | ||
| 							'register_sortable_columns',
 | ||
| 						)
 | ||
| 					);
 | ||
| 					add_action(
 | ||
| 						'pre_get_comments',
 | ||
| 						array(
 | ||
| 							'Antispam_Bee_Columns',
 | ||
| 							'set_orderby_query',
 | ||
| 						)
 | ||
| 					);
 | ||
| 					add_action(
 | ||
| 						'restrict_manage_comments',
 | ||
| 						array(
 | ||
| 							'Antispam_Bee_Columns',
 | ||
| 							'filter_columns',
 | ||
| 						)
 | ||
| 					);
 | ||
| 					add_action(
 | ||
| 						'pre_get_comments',
 | ||
| 						array(
 | ||
| 							'Antispam_Bee_Columns',
 | ||
| 							'filter_by_spam_reason',
 | ||
| 						)
 | ||
| 					);
 | ||
| 				}
 | ||
| 			}
 | ||
| 		} else {
 | ||
| 			add_action(
 | ||
| 				'wp',
 | ||
| 				array(
 | ||
| 					__CLASS__,
 | ||
| 					'populate_post_id',
 | ||
| 				)
 | ||
| 			);
 | ||
| 
 | ||
| 			if ( 1 == self::get_option( 'use_output_buffer' ) || null === self::get_option( 'use_output_buffer' ) ) {
 | ||
| 				add_action(
 | ||
| 					'template_redirect',
 | ||
| 					array(
 | ||
| 						__CLASS__,
 | ||
| 						'prepare_comment_field_output_buffering',
 | ||
| 					)
 | ||
| 				);
 | ||
| 			} else {
 | ||
| 				add_filter(
 | ||
| 					'comment_form_field_comment',
 | ||
| 					array(
 | ||
| 						__CLASS__,
 | ||
| 						'prepare_comment_field',
 | ||
| 					),
 | ||
| 					99
 | ||
| 				);
 | ||
| 			}
 | ||
| 
 | ||
| 			add_action(
 | ||
| 				'init',
 | ||
| 				array(
 | ||
| 					__CLASS__,
 | ||
| 					'precheck_incoming_request',
 | ||
| 				)
 | ||
| 			);
 | ||
| 			add_action(
 | ||
| 				'preprocess_comment',
 | ||
| 				array(
 | ||
| 					__CLASS__,
 | ||
| 					'handle_incoming_request',
 | ||
| 				),
 | ||
| 				1
 | ||
| 			);
 | ||
| 			add_action(
 | ||
| 				'antispam_bee_count',
 | ||
| 				array(
 | ||
| 					__CLASS__,
 | ||
| 					'the_spam_count',
 | ||
| 				)
 | ||
| 			);
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 	/*
 | ||
| 	*   ############################
 | ||
| 	*   ########  INSTALL  #########
 | ||
| 	*   ############################
 | ||
| 	*/
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Action during the activation of the Plugins
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 * @since  2.10.0 Set `use_output_buffer` option to `0`
 | ||
| 	 */
 | ||
| 	public static function activate() {
 | ||
| 		add_option(
 | ||
| 			'antispam_bee',
 | ||
| 			array(
 | ||
| 				'use_output_buffer' => 0,
 | ||
| 			),
 | ||
| 			'',
 | ||
| 			'no'
 | ||
| 		);
 | ||
| 
 | ||
| 		if ( self::get_option( 'cronjob_enable' ) ) {
 | ||
| 			self::init_scheduled_hook();
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Action to deactivate the plugin
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 */
 | ||
| 	public static function deactivate() {
 | ||
| 		self::clear_scheduled_hook();
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Action deleting the plugin
 | ||
| 	 *
 | ||
| 	 * @since  2.4
 | ||
| 	 */
 | ||
| 	public static function uninstall() {
 | ||
| 		if ( ! self::get_option( 'delete_data_on_uninstall' ) ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 		global $wpdb;
 | ||
| 
 | ||
| 		delete_option( 'antispam_bee' );
 | ||
| 		$wpdb->query( 'OPTIMIZE TABLE `' . $wpdb->options . '`' );
 | ||
| 
 | ||
| 		//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
 | ||
| 		$sql = 'delete from `' . $wpdb->commentmeta . '` where `meta_key` IN ("antispam_bee_iphash", "antispam_bee_reason")';
 | ||
| 		$wpdb->query( $sql );
 | ||
| 		//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 	/*
 | ||
| 	*   ############################
 | ||
| 	*   ########  INTERNAL  ########
 | ||
| 	*   ############################
 | ||
| 	*/
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Initialization of the internal variables
 | ||
| 	 *
 | ||
| 	 * @since  2.4
 | ||
| 	 * @since  2.7.0
 | ||
| 	 * @since  2.10.0 Change renamed country option names in options array
 | ||
| 	 */
 | ||
| 	private static function _init_internal_vars() {
 | ||
| 		self::$_base = plugin_basename( __FILE__ );
 | ||
| 
 | ||
| 		$salt        = defined( 'NONCE_SALT' ) ? NONCE_SALT : ABSPATH;
 | ||
| 		self::$_salt = substr( sha1( $salt ), 0, 10 );
 | ||
| 
 | ||
| 		self::$defaults = array(
 | ||
| 			'options' => array(
 | ||
| 				'regexp_check'             => 1,
 | ||
| 				'spam_ip'                  => 1,
 | ||
| 				'already_commented'        => 1,
 | ||
| 				'gravatar_check'           => 0,
 | ||
| 				'time_check'               => 0,
 | ||
| 				'ignore_pings'             => 0,
 | ||
| 
 | ||
| 				'dashboard_chart'          => 0,
 | ||
| 				'dashboard_count'          => 0,
 | ||
| 
 | ||
| 				'country_code'             => 0,
 | ||
| 				'country_denied'           => '',
 | ||
| 				'country_allowed'          => '',
 | ||
| 
 | ||
| 				'translate_api'            => 0,
 | ||
| 				'translate_lang'           => array(),
 | ||
| 
 | ||
| 				'bbcode_check'             => 1,
 | ||
| 
 | ||
| 				'flag_spam'                => 1,
 | ||
| 				'email_notify'             => 0,
 | ||
| 				'no_notice'                => 0,
 | ||
| 				'cronjob_enable'           => 0,
 | ||
| 				'cronjob_interval'         => 0,
 | ||
| 
 | ||
| 				'ignore_filter'            => 0,
 | ||
| 				'ignore_type'              => 0,
 | ||
| 
 | ||
| 				'reasons_enable'           => 0,
 | ||
| 				'ignore_reasons'           => array(),
 | ||
| 
 | ||
| 				'delete_data_on_uninstall' => 1,
 | ||
| 			),
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Adds spam reason labels to the `$defaults` array.
 | ||
| 	 *
 | ||
| 	 * That is done in an extra method instead of `_init_internal_vars`
 | ||
| 	 * so that the translations are loaded before.
 | ||
| 	 *
 | ||
| 	 * @since 2.11.2
 | ||
| 	 */
 | ||
| 	private static function add_reasons_to_defaults() {
 | ||
| 		self::$defaults['reasons'] = array(
 | ||
| 			'css'           => esc_attr__( 'Honeypot', 'antispam-bee' ),
 | ||
| 			'time'          => esc_attr__( 'Comment time', 'antispam-bee' ),
 | ||
| 			'empty'         => esc_attr__( 'Empty Data', 'antispam-bee' ),
 | ||
| 			'localdb'       => esc_attr__( 'Local DB Spam', 'antispam-bee' ),
 | ||
| 			'server'        => esc_attr__( 'Fake IP', 'antispam-bee' ),
 | ||
| 			'country'       => esc_attr__( 'Country Check', 'antispam-bee' ),
 | ||
| 			'bbcode'        => esc_attr__( 'BBCode', 'antispam-bee' ),
 | ||
| 			'lang'          => esc_attr__( 'Comment Language', 'antispam-bee' ),
 | ||
| 			'regexp'        => esc_attr__( 'Regular Expression', 'antispam-bee' ),
 | ||
| 			'title_is_name' => esc_attr__( 'Identical Post title and blog title', 'antispam-bee' ),
 | ||
| 			'manually'      => esc_attr__( 'Manually', 'antispam-bee' ),
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check and return an array key
 | ||
| 	 *
 | ||
| 	 * @since   2.4.2
 | ||
| 	 * @since   2.10.0 Only return `null` if option does not exist.
 | ||
| 	 *
 | ||
| 	 * @param   array  $array Array with values.
 | ||
| 	 * @param   string $key   Name of the key.
 | ||
| 	 * @return  mixed         Value of the requested key.
 | ||
| 	 */
 | ||
| 	public static function get_key( $array, $key ) {
 | ||
| 		if ( empty( $array ) || empty( $key ) || ! isset( $array[ $key ] ) ) {
 | ||
| 			return null;
 | ||
| 		}
 | ||
| 
 | ||
| 		return $array[ $key ];
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check if comment is a ping (pingback, trackback or something similar)
 | ||
| 	 *
 | ||
| 	 * @since   2.10.0
 | ||
| 	 *
 | ||
| 	 * @param   array $comment Treated commentary data.
 | ||
| 	 * @return  boolean        `true` if ping and `false` if classic comment
 | ||
| 	 */
 | ||
| 	public static function is_ping( $comment ) {
 | ||
| 		$types   = array( 'pingback', 'trackback', 'pings' );
 | ||
| 		$is_ping = false;
 | ||
| 
 | ||
| 		if ( in_array( self::get_key( $comment, 'comment_type' ), $types, true ) ) {
 | ||
| 			$is_ping = true;
 | ||
| 		}
 | ||
| 
 | ||
| 		return apply_filters( 'antispam_bee_is_ping', $is_ping, $comment );
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Localization of the admin pages
 | ||
| 	 *
 | ||
| 	 * @since   0.1
 | ||
| 	 * @since   2.4
 | ||
| 	 *
 | ||
| 	 * @param   string $page Mark the page.
 | ||
| 	 * @return  boolean      True on success.
 | ||
| 	 */
 | ||
| 	private static function _current_page( $page ) {
 | ||
| 		// phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
 | ||
| 		switch ( $page ) {
 | ||
| 			case 'dashboard':
 | ||
| 				return ( empty( $GLOBALS['pagenow'] ) || ( ! empty( $GLOBALS['pagenow'] ) && 'index.php' === $GLOBALS['pagenow'] ) );
 | ||
| 
 | ||
| 			case 'options':
 | ||
| 				return ( ! empty( $_GET['page'] ) && 'antispam_bee' === $_GET['page'] );
 | ||
| 
 | ||
| 			case 'plugins':
 | ||
| 				return ( ! empty( $GLOBALS['pagenow'] ) && 'plugins.php' === $GLOBALS['pagenow'] );
 | ||
| 
 | ||
| 			case 'admin-post':
 | ||
| 				return ( ! empty( $GLOBALS['pagenow'] ) && 'admin-post.php' === $GLOBALS['pagenow'] );
 | ||
| 
 | ||
| 			case 'edit-comments':
 | ||
| 				return ( ! empty( $GLOBALS['pagenow'] ) && 'edit-comments.php' === $GLOBALS['pagenow'] );
 | ||
| 
 | ||
| 			default:
 | ||
| 				return false;
 | ||
| 		}
 | ||
| 		// phpcs:enable WordPress.CSRF.NonceVerification.NoNonceVerification
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Integration of the localization file
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 */
 | ||
| 	public static function load_plugin_lang() {
 | ||
| 		load_plugin_textdomain(
 | ||
| 			'antispam-bee'
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Add the link to the settings
 | ||
| 	 *
 | ||
| 	 * @since  1.1
 | ||
| 	 *
 | ||
| 	 * @param array $data The action link array.
 | ||
| 	 * @return array $data The action link array.
 | ||
| 	 */
 | ||
| 	public static function init_action_links( $data ) {
 | ||
| 		if ( ! current_user_can( 'manage_options' ) ) {
 | ||
| 			return $data;
 | ||
| 		}
 | ||
| 
 | ||
| 		return array_merge(
 | ||
| 			$data,
 | ||
| 			array(
 | ||
| 				sprintf(
 | ||
| 					'<a href="%s">%s</a>',
 | ||
| 					add_query_arg(
 | ||
| 						array(
 | ||
| 							'page' => 'antispam_bee',
 | ||
| 						),
 | ||
| 						admin_url( 'options-general.php' )
 | ||
| 					),
 | ||
| 					esc_attr__( 'Settings', 'antispam-bee' )
 | ||
| 				),
 | ||
| 			)
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Meta links of the plugin
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.6.2
 | ||
| 	 *
 | ||
| 	 * @param   array  $input Existing links.
 | ||
| 	 * @param   string $file  Current page.
 | ||
| 	 * @return  array  $data  Modified links.
 | ||
| 	 */
 | ||
| 	public static function init_row_meta( $input, $file ) {
 | ||
| 		if ( $file !== self::$_base ) {
 | ||
| 			return $input;
 | ||
| 		}
 | ||
| 
 | ||
| 		return array_merge(
 | ||
| 			$input,
 | ||
| 			array(
 | ||
| 				'<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=TD4AMD2D8EMZW" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Donate', 'antispam-bee' ) . '</a>',
 | ||
| 				'<a href="https://wordpress.org/support/plugin/antispam-bee" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Support', 'antispam-bee' ) . '</a>',
 | ||
| 			)
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/*
 | ||
| 	*   ############################
 | ||
| 	*   #######  RESOURCES  ########
 | ||
| 	*   ############################
 | ||
| 	*/
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Registration of resources (CSS & JS)
 | ||
| 	 *
 | ||
| 	 * @since  1.6
 | ||
| 	 * @since  2.4.5
 | ||
| 	 */
 | ||
| 	public static function init_plugin_sources() {
 | ||
| 		$plugin = get_plugin_data( __FILE__ );
 | ||
| 
 | ||
| 		wp_register_script(
 | ||
| 			'ab_script',
 | ||
| 			plugins_url( 'js/scripts.min.js', __FILE__ ),
 | ||
| 			array( 'jquery' ),
 | ||
| 			$plugin['Version']
 | ||
| 		);
 | ||
| 
 | ||
| 		wp_register_style(
 | ||
| 			'ab_style',
 | ||
| 			plugins_url( 'css/styles.min.css', __FILE__ ),
 | ||
| 			array( 'dashicons' ),
 | ||
| 			$plugin['Version']
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Initialization of the option page
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4.3
 | ||
| 	 */
 | ||
| 	public static function add_sidebar_menu() {
 | ||
| 		$page = add_options_page(
 | ||
| 			'Antispam Bee',
 | ||
| 			'Antispam Bee',
 | ||
| 			'manage_options',
 | ||
| 			'antispam_bee',
 | ||
| 			array(
 | ||
| 				'Antispam_Bee_GUI',
 | ||
| 				'options_page',
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		add_action(
 | ||
| 			'admin_print_scripts-' . $page,
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'add_options_script',
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		add_action(
 | ||
| 			'admin_print_styles-' . $page,
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'add_options_style',
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		add_action(
 | ||
| 			'load-' . $page,
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'init_options_page',
 | ||
| 			)
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Initialization of JavaScript
 | ||
| 	 *
 | ||
| 	 * @since  1.6
 | ||
| 	 * @since  2.4
 | ||
| 	 */
 | ||
| 	public static function add_options_script() {
 | ||
| 		wp_enqueue_script( 'ab_script' );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Initialization of Stylesheets
 | ||
| 	 *
 | ||
| 	 * @since  1.6
 | ||
| 	 * @since  2.4
 | ||
| 	 */
 | ||
| 	public static function add_options_style() {
 | ||
| 		wp_enqueue_style( 'ab_style' );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Integration of the GUI
 | ||
| 	 *
 | ||
| 	 * @since   2.4
 | ||
| 	 */
 | ||
| 	public static function init_options_page() {
 | ||
| 		require_once dirname( __FILE__ ) . '/inc/gui.class.php';
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 	/*
 | ||
| 	*   ############################
 | ||
| 	*   #######  DASHBOARD  ########
 | ||
| 	*   ############################
 | ||
| 	*/
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Display the spam counter on the dashboard
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.6.5
 | ||
| 	 *
 | ||
| 	 * @param   array $items  Initial array with dashboard items.
 | ||
| 	 * @return  array $items  Merged array with dashboard items.
 | ||
| 	 */
 | ||
| 	public static function add_dashboard_count( $items = array() ) {
 | ||
| 		if ( ! current_user_can( 'manage_options' ) || ! self::get_option( 'dashboard_count' ) ) {
 | ||
| 			return $items;
 | ||
| 		}
 | ||
| 
 | ||
| 		echo '<style>#dashboard_right_now .ab-count:before {content: "\f117"}</style>';
 | ||
| 
 | ||
| 		$items[] = '<span class="ab-count">' . esc_html(
 | ||
| 			sprintf(
 | ||
| 				// translators: The number of spam comments Antispam Bee blocked so far.
 | ||
| 				__( '%s Blocked', 'antispam-bee' ),
 | ||
| 				self::_get_spam_count()
 | ||
| 			)
 | ||
| 		) . '</span>';
 | ||
| 
 | ||
| 		return $items;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Initialize the dashboard chart
 | ||
| 	 *
 | ||
| 	 * @since  1.9
 | ||
| 	 * @since  2.5.6
 | ||
| 	 */
 | ||
| 	public static function add_dashboard_chart() {
 | ||
| 		if ( ! current_user_can( 'publish_posts' ) || ! self::get_option( 'dashboard_chart' ) ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		wp_add_dashboard_widget(
 | ||
| 			'ab_widget',
 | ||
| 			'Antispam Bee',
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'show_spam_chart',
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		add_action(
 | ||
| 			'admin_head',
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'add_dashboard_style',
 | ||
| 			)
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Print dashboard styles
 | ||
| 	 *
 | ||
| 	 * @since  1.9.0
 | ||
| 	 * @since  2.5.8
 | ||
| 	 */
 | ||
| 	public static function add_dashboard_style() {
 | ||
| 		$plugin = get_plugin_data( __FILE__ );
 | ||
| 
 | ||
| 		wp_register_style(
 | ||
| 			'ab_chart',
 | ||
| 			plugins_url( 'css/dashboard.min.css', __FILE__ ),
 | ||
| 			array(),
 | ||
| 			$plugin['Version']
 | ||
| 		);
 | ||
| 
 | ||
| 		wp_print_styles( 'ab_chart' );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Print dashboard scripts
 | ||
| 	 *
 | ||
| 	 * @since  1.9.0
 | ||
| 	 * @since  2.5.8
 | ||
| 	 */
 | ||
| 	public static function add_dashboard_script() {
 | ||
| 		if ( ! self::get_option( 'daily_stats' ) ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		$plugin = get_plugin_data( __FILE__ );
 | ||
| 
 | ||
| 		wp_enqueue_script(
 | ||
| 			'raphael',
 | ||
| 			plugins_url( 'js/raphael.min.js', __FILE__ ),
 | ||
| 			array(),
 | ||
| 			'2.1.0',
 | ||
| 			true
 | ||
| 		);
 | ||
| 
 | ||
| 		wp_enqueue_script(
 | ||
| 			'ab-raphael',
 | ||
| 			plugins_url( 'js/raphael.helper.min.js', __FILE__ ),
 | ||
| 			array( 'raphael' ),
 | ||
| 			$plugin['Version'],
 | ||
| 			true
 | ||
| 		);
 | ||
| 
 | ||
| 		wp_enqueue_script(
 | ||
| 			'ab_chart_js',
 | ||
| 			plugins_url( 'js/dashboard.min.js', __FILE__ ),
 | ||
| 			array( 'jquery', 'ab-raphael' ),
 | ||
| 			$plugin['Version'],
 | ||
| 			true
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Print dashboard html
 | ||
| 	 *
 | ||
| 	 * @since  1.9.0
 | ||
| 	 * @since  2.5.8
 | ||
| 	 */
 | ||
| 	public static function show_spam_chart() {
 | ||
| 		$items = (array) self::get_option( 'daily_stats' );
 | ||
| 
 | ||
| 		if ( empty( $items ) ) {
 | ||
| 			echo sprintf(
 | ||
| 				'<div id="ab_chart"><p>%s</p></div>',
 | ||
| 				esc_html__( 'No data available.', 'antispam-bee' )
 | ||
| 			);
 | ||
| 
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		self::add_dashboard_script();
 | ||
| 
 | ||
| 		ksort( $items, SORT_NUMERIC );
 | ||
| 
 | ||
| 		$html = "<table id=ab_chart_data>\n";
 | ||
| 
 | ||
| 		$html .= "<tfoot><tr>\n";
 | ||
| 		foreach ( $items as $date => $count ) {
 | ||
| 			$html .= '<th>' . date_i18n( 'j. F Y', $date ) . "</th>\n";
 | ||
| 		}
 | ||
| 		$html .= "</tr></tfoot>\n";
 | ||
| 
 | ||
| 		$html .= "<tbody><tr>\n";
 | ||
| 		foreach ( $items as $date => $count ) {
 | ||
| 			$html .= '<td>' . (int) $count . "</td>\n";
 | ||
| 		}
 | ||
| 		$html .= "</tr></tbody>\n";
 | ||
| 
 | ||
| 		$html .= "</table>\n";
 | ||
| 
 | ||
| 		echo wp_kses_post( '<div id="ab_chart">' . $html . '</div>' );
 | ||
| 	}
 | ||
| 
 | ||
| 	/*
 | ||
| 	*   ############################
 | ||
| 	*   ########  OPTIONS  #########
 | ||
| 	*   ############################
 | ||
| 	*/
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Get all plugin options
 | ||
| 	 *
 | ||
| 	 * @since  2.4
 | ||
| 	 * @since  2.6.1
 | ||
| 	 *
 | ||
| 	 * @return  array $options Array with option fields.
 | ||
| 	 */
 | ||
| 	public static function get_options() {
 | ||
| 		$options = wp_cache_get( 'antispam_bee' );
 | ||
| 		if ( ! $options ) {
 | ||
| 			wp_cache_set(
 | ||
| 				'antispam_bee',
 | ||
| 				$options = get_option( 'antispam_bee' )
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( null === self::$defaults ) {
 | ||
| 			self::_init_internal_vars();
 | ||
| 		}
 | ||
| 
 | ||
| 		return wp_parse_args(
 | ||
| 			$options,
 | ||
| 			self::$defaults['options']
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Get single option field
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4.2
 | ||
| 	 *
 | ||
| 	 * @param   string $field Field name.
 | ||
| 	 * @return  mixed         Field value.
 | ||
| 	 */
 | ||
| 	public static function get_option( $field ) {
 | ||
| 		$options = self::get_options();
 | ||
| 
 | ||
| 		return self::get_key( $options, $field );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Update single option field
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 *
 | ||
| 	 * @param   string $field Field name.
 | ||
| 	 * @param   mixed  $value The Field value.
 | ||
| 	 */
 | ||
| 	private static function _update_option( $field, $value ) {
 | ||
| 		self::update_options(
 | ||
| 			array(
 | ||
| 				$field => $value,
 | ||
| 			)
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Update multiple option fields
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.6.1
 | ||
| 	 *
 | ||
| 	 * @param   array $data Array with plugin option fields.
 | ||
| 	 */
 | ||
| 	public static function update_options( $data ) {
 | ||
| 		$options = get_option( 'antispam_bee' );
 | ||
| 
 | ||
| 		if ( is_array( $options ) ) {
 | ||
| 			$options = array_merge(
 | ||
| 				$options,
 | ||
| 				$data
 | ||
| 			);
 | ||
| 		} else {
 | ||
| 			$options = $data;
 | ||
| 		}
 | ||
| 
 | ||
| 		update_option(
 | ||
| 			'antispam_bee',
 | ||
| 			$options
 | ||
| 		);
 | ||
| 
 | ||
| 		wp_cache_set(
 | ||
| 			'antispam_bee',
 | ||
| 			$options
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 	/*
 | ||
| 	*   ############################
 | ||
| 	*   ########  CRONJOBS  ########
 | ||
| 	*   ############################
 | ||
| 	*/
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Execution of the daily cronjobs
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 */
 | ||
| 	public static function start_daily_cronjob() {
 | ||
| 		if ( ! self::get_option( 'cronjob_enable' ) ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		self::_update_option(
 | ||
| 			'cronjob_timestamp',
 | ||
| 			time()
 | ||
| 		);
 | ||
| 
 | ||
| 		self::_delete_old_spam();
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Delete old spam comments
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 */
 | ||
| 	private static function _delete_old_spam() {
 | ||
| 		$days = (int) self::get_option( 'cronjob_interval' );
 | ||
| 
 | ||
| 		if ( empty( $days ) ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		global $wpdb;
 | ||
| 
 | ||
| 		$wpdb->query(
 | ||
| 			$wpdb->prepare(
 | ||
| 				"DELETE c, cm FROM `$wpdb->comments` AS c LEFT JOIN `$wpdb->commentmeta` AS cm ON (c.comment_ID = cm.comment_id) WHERE c.comment_approved = 'spam' AND SUBDATE(NOW(), %d) > c.comment_date_gmt",
 | ||
| 				$days
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		$wpdb->query( "OPTIMIZE TABLE `$wpdb->comments`" );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Initialization of the cronjobs
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 */
 | ||
| 	public static function init_scheduled_hook() {
 | ||
| 		if ( ! wp_next_scheduled( 'antispam_bee_daily_cronjob' ) ) {
 | ||
| 			wp_schedule_event(
 | ||
| 				time(),
 | ||
| 				'daily',
 | ||
| 				'antispam_bee_daily_cronjob'
 | ||
| 			);
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Deletion of the cronjobs
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 */
 | ||
| 	public static function clear_scheduled_hook() {
 | ||
| 		if ( wp_next_scheduled( 'antispam_bee_daily_cronjob' ) ) {
 | ||
| 			wp_clear_scheduled_hook( 'antispam_bee_daily_cronjob' );
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Shows plugin update notice
 | ||
| 	 *
 | ||
| 	 * @since  2.11.4
 | ||
| 	 *
 | ||
| 	 * @param array $data An array of plugin metadata. See get_plugin_data()
 | ||
| 	 *                    and the {@see 'plugin_row_meta'} filter for the list
 | ||
| 	 *                    of possible values.
 | ||
| 	 *
 | ||
| 	 * @return void
 | ||
| 	 */
 | ||
| 	public static function upgrade_notice( $data ) {
 | ||
| 		if ( isset( $data['upgrade_notice'] ) ) {
 | ||
| 			printf(
 | ||
| 				'<div class="update-message">%s</div>',
 | ||
| 				wp_kses(
 | ||
| 					wpautop( $data['upgrade_notice '] ),
 | ||
| 					array(
 | ||
| 						'p'     => array(),
 | ||
| 						'a'     => array( 'href', 'title' ),
 | ||
| 						'strong' => array(),
 | ||
| 						'em' => array(),
 | ||
| 					)
 | ||
| 				)
 | ||
| 			);
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/*
 | ||
| 	*   ############################
 | ||
| 	*   ######  SPAM CHECK  ########
 | ||
| 	*   ############################
 | ||
| 	*/
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check POST values
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.6.3
 | ||
| 	 */
 | ||
| 	public static function precheck_incoming_request() {
 | ||
| 		// phpcs:disable WordPress.Security.NonceVerification.Missing
 | ||
| 		if ( is_feed() || is_trackback() || empty( $_POST ) || self::_is_mobile() ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		$request_uri  = self::get_key( $_SERVER, 'REQUEST_URI' );
 | ||
| 		$request_path = self::parse_url( $request_uri, 'path' );
 | ||
| 
 | ||
| 		if ( strpos( $request_path, 'wp-comments-post.php' ) === false ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		$post_id      = (int) self::get_key( $_POST, 'comment_post_ID' );
 | ||
| 		$hidden_field = self::get_key( $_POST, 'comment' );
 | ||
| 		$plugin_field = self::get_key( $_POST, self::get_secret_name_for_post( $post_id ) );
 | ||
| 
 | ||
| 		if ( ! empty( $hidden_field ) ) {
 | ||
| 			$_POST['ab_spam__hidden_field'] = 1;
 | ||
| 		} else {
 | ||
| 			$_POST['comment'] = $plugin_field;
 | ||
| 			unset( $_POST[ self::get_secret_name_for_post( $post_id ) ] );
 | ||
| 		}
 | ||
| 		// phpcs:enable WordPress.Security.NonceVerification.Missing
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check incoming requests for spam
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.6.3
 | ||
| 	 * @since  2.10.0 Refactoring of code if pings are allowed and if is ping
 | ||
| 	 *
 | ||
| 	 * @param   array $comment  Untreated comment.
 | ||
| 	 * @return  array $comment  Treated comment.
 | ||
| 	 */
 | ||
| 	public static function handle_incoming_request( $comment ) {
 | ||
| 		$comment['comment_author_IP'] = self::get_client_ip();
 | ||
| 
 | ||
| 		$request_uri  = self::get_key( $_SERVER, 'REQUEST_URI' );
 | ||
| 		$request_path = self::parse_url( $request_uri, 'path' );
 | ||
| 
 | ||
| 		if ( empty( $request_path ) ) {
 | ||
| 			return self::_handle_spam_request(
 | ||
| 				$comment,
 | ||
| 				'empty'
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		$pings_allowed = ! self::get_option( 'ignore_pings' );
 | ||
| 
 | ||
| 		// phpcs:disable WordPress.Security.NonceVerification.Missing
 | ||
| 		// Everybody can post.
 | ||
| 		if ( strpos( $request_path, 'wp-comments-post.php' ) !== false && ! empty( $_POST ) ) {
 | ||
| 			// phpcs:enable WordPress.Security.NonceVerification.Missing
 | ||
| 			$status = self::_verify_comment_request( $comment );
 | ||
| 
 | ||
| 			if ( ! empty( $status['reason'] ) ) {
 | ||
| 				return self::_handle_spam_request(
 | ||
| 					$comment,
 | ||
| 					$status['reason']
 | ||
| 				);
 | ||
| 			}
 | ||
| 		} elseif ( self::is_ping( $comment ) && $pings_allowed ) {
 | ||
| 			$status = self::_verify_trackback_request( $comment );
 | ||
| 
 | ||
| 			if ( ! empty( $status['reason'] ) ) {
 | ||
| 				return self::_handle_spam_request(
 | ||
| 					$comment,
 | ||
| 					$status['reason'],
 | ||
| 					true
 | ||
| 				);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		return $comment;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Prepares the replacement of the comment field with output buffering.
 | ||
| 	 *
 | ||
| 	 * @since 2.10.0
 | ||
| 	 */
 | ||
| 	public static function prepare_comment_field_output_buffering() {
 | ||
| 		if ( is_feed() || is_trackback() || is_robots() || self::_is_mobile() ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		ob_start(
 | ||
| 			array(
 | ||
| 				'Antispam_Bee',
 | ||
| 				'prepare_comment_field',
 | ||
| 			)
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Prepares the replacement of the comment field
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 * @since  2.10.0 Changes needed because of new way to add the honeypot field via filter instead of output buffering
 | ||
| 	 *
 | ||
| 	 * @param string $data Markup of the comment field or whole page (depending on ob option).
 | ||
| 	 */
 | ||
| 	public static function prepare_comment_field( $data ) {
 | ||
| 		if ( empty( $data ) ) {
 | ||
| 			return $data;
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( ! preg_match( '#<textarea.+?name=["\']comment["\']#s', $data ) ) {
 | ||
| 			return $data;
 | ||
| 		}
 | ||
| 
 | ||
| 		return preg_replace_callback(
 | ||
| 			'/(?P<all>                                                              (?# match the whole textarea tag )
 | ||
| 				<textarea                                                           (?# the opening of the textarea and some optional attributes )
 | ||
| 				(                                                                   (?# match a id attribute followed by some optional ones and the name attribute )
 | ||
| 					(?P<before1>[^>]*)
 | ||
| 					(?P<id1>id=["\'](?P<id_value1>[^>"\']*)["\'])
 | ||
| 					(?P<between1>[^>]*)
 | ||
| 					name=["\']comment["\']
 | ||
| 					|                                                               (?# match same as before, but with the name attribute before the id attribute )
 | ||
| 					(?P<before2>[^>]*)
 | ||
| 					name=["\']comment["\']
 | ||
| 					(?P<between2>[^>]*)
 | ||
| 					(?P<id2>id=["\'](?P<id_value2>[^>"\']*)["\'])
 | ||
| 					|                                                               (?# match same as before, but with no id attribute )
 | ||
| 					(?P<before3>[^>]*)
 | ||
| 					name=["\']comment["\']
 | ||
| 					(?P<between3>[^>]*)
 | ||
| 				)
 | ||
| 				(?P<after>[^>]*)                                                    (?# match any additional optional attributes )
 | ||
| 				>                                                                   (?# the closing of the textarea opening tag )
 | ||
| 				(?s)(?P<content>.*?)                                                (?# any textarea content )
 | ||
| 				<\/textarea>                                                        (?# the closing textarea tag )
 | ||
| 			)/x',
 | ||
| 			array( 'Antispam_Bee', 'replace_comment_field_callback' ),
 | ||
| 			$data,
 | ||
| 			-1
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * The callback function for the preg_match_callback to modify the textarea tags.
 | ||
| 	 *
 | ||
| 	 * @since   2.6.10
 | ||
| 	 *
 | ||
| 	 * @param array $matches The regex matches.
 | ||
| 	 *
 | ||
| 	 * @return string The modified content string.
 | ||
| 	 */
 | ||
| 	public static function replace_comment_field_callback( $matches ) {
 | ||
| 		if ( self::get_option( 'time_check' ) ) {
 | ||
| 			$init_time_field = sprintf(
 | ||
| 				'<input type="hidden" name="ab_init_time" value="%d" />',
 | ||
| 				time()
 | ||
| 			);
 | ||
| 		} else {
 | ||
| 			$init_time_field = '';
 | ||
| 		}
 | ||
| 
 | ||
| 		$output = '<textarea autocomplete="new-password" ' . $matches['before1'] . $matches['before2'] . $matches['before3'];
 | ||
| 
 | ||
| 		$id_script = '';
 | ||
| 		if ( ! empty( $matches['id1'] ) || ! empty( $matches['id2'] ) ) {
 | ||
| 			$output .= 'id="' . self::get_secret_id_for_post( self::$_current_post_id ) . '" ';
 | ||
| 			if ( ! self::_is_amp() ) {
 | ||
| 				$id_script = '<script data-noptimize>' .
 | ||
| 							'document.getElementById("comment").setAttribute( "id", "a' . substr( esc_js( md5( time() ) ), 0, 31 ) . '" );' .
 | ||
| 							'document.getElementById("' . esc_js( self::get_secret_id_for_post( self::$_current_post_id ) ) . '").setAttribute( "id", "comment" );' .
 | ||
| 							'</script>';
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		$output .= ' name="' . esc_attr( self::get_secret_name_for_post( self::$_current_post_id ) ) . '" ';
 | ||
| 		$output .= $matches['between1'] . $matches['between2'] . $matches['between3'];
 | ||
| 		$output .= $matches['after'] . '>';
 | ||
| 		$output .= $matches['content'];
 | ||
| 		$output .= '</textarea><textarea id="comment" aria-label="hp-comment" aria-hidden="true" name="comment" autocomplete="new-password" style="padding:0 !important;clip:rect(1px, 1px, 1px, 1px) !important;position:absolute !important;white-space:nowrap !important;height:1px !important;width:1px !important;overflow:hidden !important;" tabindex="-1"></textarea>';
 | ||
| 
 | ||
| 		$output .= $id_script;
 | ||
| 		$output .= $init_time_field;
 | ||
| 
 | ||
| 		return $output;
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check the trackbacks
 | ||
| 	 *
 | ||
| 	 * @since  2.4
 | ||
| 	 * @since  2.7.0
 | ||
| 	 *
 | ||
| 	 * @param   array $comment Trackback data.
 | ||
| 	 * @return  array          Array with suspected reason.
 | ||
| 	 */
 | ||
| 	private static function _verify_trackback_request( $comment ) {
 | ||
| 		$ip        = self::get_key( $comment, 'comment_author_IP' );
 | ||
| 		$url       = self::get_key( $comment, 'comment_author_url' );
 | ||
| 		$body      = self::get_key( $comment, 'comment_content' );
 | ||
| 		$post_id   = self::get_key( $comment, 'comment_post_ID' );
 | ||
| 		$type      = self::get_key( $comment, 'comment_type' );
 | ||
| 		$blog_name = self::get_key( $comment, 'comment_author' );
 | ||
| 
 | ||
| 		if ( empty( $url ) || empty( $body ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'empty',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( empty( $ip ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'empty',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( 'pingback' === $type && self::_pingback_from_myself( $url, $post_id ) ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( self::is_trackback_post_title_blog_name_spam( $body, $blog_name ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'title_is_name',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		$options = self::get_options();
 | ||
| 
 | ||
| 		if ( $options['bbcode_check'] && self::_is_bbcode_spam( $body ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'bbcode',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $options['spam_ip'] && self::_is_db_spam( $ip, $url ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'localdb',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $options['country_code'] && self::_is_country_spam( $ip ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'country',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $options['translate_api'] && self::_is_lang_spam( $body ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'lang',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $options['regexp_check'] && self::_is_regexp_spam(
 | ||
| 			array(
 | ||
| 				'ip'     => $ip,
 | ||
| 				'rawurl' => $url,
 | ||
| 				'host'   => self::parse_url( $url, 'host' ),
 | ||
| 				'body'   => $body,
 | ||
| 				'email'  => '',
 | ||
| 				'author' => '',
 | ||
| 			)
 | ||
| 		) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'regexp',
 | ||
| 			);
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check, if I pinged myself.
 | ||
| 	 *
 | ||
| 	 * @since 2.8.2
 | ||
| 	 *
 | ||
| 	 * @param string $url            The URL from where the ping came.
 | ||
| 	 * @param int    $target_post_id The post ID which has been pinged.
 | ||
| 	 *
 | ||
| 	 * @return bool
 | ||
| 	 */
 | ||
| 	private static function _pingback_from_myself( $url, $target_post_id ) {
 | ||
| 
 | ||
| 		if ( 0 !== strpos( $url, home_url() ) ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$original_post_id = (int) url_to_postid( $url );
 | ||
| 		if ( ! $original_post_id ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$post = get_post( $original_post_id );
 | ||
| 		if ( ! $post ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$urls        = wp_extract_urls( $post->post_content );
 | ||
| 		$url_to_find = get_permalink( $target_post_id );
 | ||
| 		if ( ! $url_to_find ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 		foreach ( $urls as $url ) {
 | ||
| 			if ( strpos( $url, $url_to_find ) === 0 ) {
 | ||
| 				return true;
 | ||
| 			}
 | ||
| 		}
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check the comment
 | ||
| 	 *
 | ||
| 	 * @since  2.4
 | ||
| 	 * @since  2.7.0
 | ||
| 	 * @since  2.10.0 Add useragent as data to regex check
 | ||
| 	 *
 | ||
| 	 * @param   array $comment Data of the comment.
 | ||
| 	 * @return  array|void     Array with suspected reason
 | ||
| 	 */
 | ||
| 	private static function _verify_comment_request( $comment ) {
 | ||
| 		$ip        = self::get_key( $comment, 'comment_author_IP' );
 | ||
| 		$url       = self::get_key( $comment, 'comment_author_url' );
 | ||
| 		$body      = self::get_key( $comment, 'comment_content' );
 | ||
| 		$email     = self::get_key( $comment, 'comment_author_email' );
 | ||
| 		$author    = self::get_key( $comment, 'comment_author' );
 | ||
| 		$useragent = self::get_key( $comment, 'comment_agent' );
 | ||
| 
 | ||
| 		// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
 | ||
| 		$allow_empty_comment = apply_filters( 'allow_empty_comment', false, $comment );
 | ||
| 
 | ||
| 		if ( empty( $body ) && ! $allow_empty_comment ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'empty',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( empty( $ip ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'empty',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( get_option( 'require_name_email' ) && ( empty( $email ) || empty( $author ) ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'empty',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		$options = self::get_options();
 | ||
| 
 | ||
| 		if ( $options['already_commented'] && ! empty( $email ) && self::_is_approved_email( $email ) ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $options['gravatar_check'] && ! empty( $email ) && 1 === (int) get_option( 'show_avatars', 0 ) && self::_has_valid_gravatar( $email ) ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		// phpcs:disable WordPress.Security.NonceVerification.Missing
 | ||
| 		if ( ! empty( $_POST['ab_spam__hidden_field'] ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'css',
 | ||
| 			);
 | ||
| 		}
 | ||
| 		// phpcs:enable WordPress.Security.NonceVerification.Missing
 | ||
| 
 | ||
| 		if ( $options['time_check'] && self::_is_shortest_time() ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'time',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $options['bbcode_check'] && self::_is_bbcode_spam( $body ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'bbcode',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $options['regexp_check'] && self::_is_regexp_spam(
 | ||
| 			array(
 | ||
| 				'ip'        => $ip,
 | ||
| 				'rawurl'    => $url,
 | ||
| 				'host'      => self::parse_url( $url, 'host' ),
 | ||
| 				'body'      => $body,
 | ||
| 				'email'     => $email,
 | ||
| 				'author'    => $author,
 | ||
| 				'useragent' => $useragent,
 | ||
| 			)
 | ||
| 		) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'regexp',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $options['spam_ip'] && self::_is_db_spam( $ip, $url, $email ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'localdb',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $options['country_code'] && self::_is_country_spam( $ip ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'country',
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $options['translate_api'] && self::_is_lang_spam( $body ) ) {
 | ||
| 			return array(
 | ||
| 				'reason' => 'lang',
 | ||
| 			);
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check for a Gravatar image
 | ||
| 	 *
 | ||
| 	 * @since  2.6.5
 | ||
| 	 *
 | ||
| 	 * @param   string $email Input email.
 | ||
| 	 * @return  boolean       Check status (true = Gravatar available).
 | ||
| 	 */
 | ||
| 	private static function _has_valid_gravatar( $email ) {
 | ||
| 		$response = wp_safe_remote_get(
 | ||
| 			sprintf(
 | ||
| 				'https://www.gravatar.com/avatar/%s?d=404',
 | ||
| 				md5( strtolower( trim( $email ) ) )
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		if ( is_wp_error( $response ) ) {
 | ||
| 			return null;
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( wp_remote_retrieve_response_code( $response ) === 200 ) {
 | ||
| 			return true;
 | ||
| 		}
 | ||
| 
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check for comment action time
 | ||
| 	 *
 | ||
| 	 * @since   2.6.4
 | ||
| 	 *
 | ||
| 	 * @return  boolean    TRUE if the action time is less than 5 seconds
 | ||
| 	 */
 | ||
| 	private static function _is_shortest_time() {
 | ||
| 		// phpcs:disable WordPress.Security.NonceVerification.Missing
 | ||
| 		// Everybody can Post.
 | ||
| 		$init_time = (int) self::get_key( $_POST, 'ab_init_time' );
 | ||
| 		// phpcs:enable WordPress.Security.NonceVerification.Missing
 | ||
| 		if ( 0 === $init_time ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( time() - $init_time < apply_filters( 'ab_action_time_limit', 5 ) ) {
 | ||
| 			return true;
 | ||
| 		}
 | ||
| 
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check if the blog name and the title of the blog post from which the trackback originates are equal.
 | ||
| 	 *
 | ||
| 	 * @since   2.6.4
 | ||
| 	 *
 | ||
| 	 * @param string $body      The comment body.
 | ||
| 	 * @param string $blog_name The name of the blog.
 | ||
| 	 *
 | ||
| 	 * @return bool
 | ||
| 	 */
 | ||
| 	private static function is_trackback_post_title_blog_name_spam( $body, $blog_name ) {
 | ||
| 		preg_match( '/<strong>(.*)<\/strong>\\n\\n/', $body, $matches );
 | ||
| 		if ( ! isset( $matches[1] ) ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 		return trim( $matches[1] ) === trim( $blog_name );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Usage of regexp, also custom
 | ||
| 	 *
 | ||
| 	 * @since  2.5.2
 | ||
| 	 * @since  2.5.6
 | ||
| 	 * @since  2.10.0 Use useragent in check
 | ||
| 	 *
 | ||
| 	 * @param   array $comment Array with commentary data.
 | ||
| 	 * @return  boolean        True for suspicious comment.
 | ||
| 	 */
 | ||
| 	private static function _is_regexp_spam( $comment ) {
 | ||
| 		$fields = array(
 | ||
| 			'ip',
 | ||
| 			'host',
 | ||
| 			'body',
 | ||
| 			'email',
 | ||
| 			'author',
 | ||
| 			'useragent',
 | ||
| 		);
 | ||
| 
 | ||
| 		$patterns = array(
 | ||
| 			array(
 | ||
| 				'host'  => '^(www\.)?\d+\w+\.com$',
 | ||
| 				'body'  => '^\w+\s\d+$',
 | ||
| 				'email' => '@gmail.com$',
 | ||
| 			),
 | ||
| 			array(
 | ||
| 				'body'   => '\b[a-z]{30}\b',
 | ||
| 				'author' => '\b[a-z]{10}\b',
 | ||
| 				'host'   => '\b[a-z]{10}\b',
 | ||
| 			),
 | ||
| 			array(
 | ||
| 				'body' => '\<\!.+?mfunc.+?\>',
 | ||
| 			),
 | ||
| 			array(
 | ||
| 				'author' => 'moncler|north face|vuitton|handbag|burberry|outlet|prada|cialis|viagra|maillot|oakley|ralph lauren|ray ban|iphone|プラダ',
 | ||
| 			),
 | ||
| 			array(
 | ||
| 				'host' => '^(www\.)?fkbook\.co\.uk$|^(www\.)?nsru\.net$|^(www\.)?goo\.gl$|^(www\.)?bit\.ly$',
 | ||
| 			),
 | ||
| 			array(
 | ||
| 				'body' => 'target[t]?ed (visitors|traffic)|viagra|cialis',
 | ||
| 			),
 | ||
| 			array(
 | ||
| 				'body' => 'purchase amazing|buy amazing|luxurybrandsale',
 | ||
| 			),
 | ||
| 			array(
 | ||
| 				'body'  => 'dating|sex|lotto|pharmacy',
 | ||
| 				'email' => '@mail\.ru|@yandex\.',
 | ||
| 			),
 | ||
| 		);
 | ||
| 
 | ||
| 		$quoted_author = preg_quote( $comment['author'], '/' );
 | ||
| 		if ( $quoted_author ) {
 | ||
| 			$patterns[] = array(
 | ||
| 				'body' => sprintf(
 | ||
| 					'<a.+?>%s<\/a>$',
 | ||
| 					$quoted_author
 | ||
| 				),
 | ||
| 			);
 | ||
| 			$patterns[] = array(
 | ||
| 				'body' => sprintf(
 | ||
| 					'%s https?:.+?$',
 | ||
| 					$quoted_author
 | ||
| 				),
 | ||
| 			);
 | ||
| 			$patterns[] = array(
 | ||
| 				'email'  => '@gmail.com$',
 | ||
| 				'author' => '^[a-z0-9-\.]+\.[a-z]{2,6}$',
 | ||
| 				'host'   => sprintf(
 | ||
| 					'^%s$',
 | ||
| 					$quoted_author
 | ||
| 				),
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		$patterns = apply_filters(
 | ||
| 			'antispam_bee_patterns',
 | ||
| 			$patterns
 | ||
| 		);
 | ||
| 
 | ||
| 		if ( ! $patterns ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		foreach ( $patterns as $pattern ) {
 | ||
| 			$hits = array();
 | ||
| 
 | ||
| 			foreach ( $pattern as $field => $regexp ) {
 | ||
| 				if ( empty( $field ) || ! in_array( $field, $fields, true ) || empty( $regexp ) ) {
 | ||
| 					continue;
 | ||
| 				}
 | ||
| 
 | ||
| 				$comment[ $field ] = ( function_exists( 'iconv' ) ? iconv( 'utf-8', 'utf-8//TRANSLIT', $comment[ $field ] ) : $comment[ $field ] );
 | ||
| 
 | ||
| 				if ( empty( $comment[ $field ] ) ) {
 | ||
| 					continue;
 | ||
| 				}
 | ||
| 
 | ||
| 				if ( preg_match( '/' . $regexp . '/isu', $comment[ $field ] ) ) {
 | ||
| 					$hits[ $field ] = true;
 | ||
| 				}
 | ||
| 			}
 | ||
| 
 | ||
| 			if ( count( $hits ) === count( $pattern ) ) {
 | ||
| 				return true;
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Review a comment on its existence in the local spam
 | ||
| 	 *
 | ||
| 	 * @since  2.0.0
 | ||
| 	 * @since  2.5.4
 | ||
| 	 *
 | ||
| 	 * @param   string $ip    Comment IP.
 | ||
| 	 * @param   string $url   Comment URL (optional).
 | ||
| 	 * @param   string $email Comment Email (optional).
 | ||
| 	 * @return  boolean       True for suspicious comment.
 | ||
| 	 */
 | ||
| 	private static function _is_db_spam( $ip, $url = '', $email = '' ) {
 | ||
| 		global $wpdb;
 | ||
| 
 | ||
| 		$params = array();
 | ||
| 		$filter = array();
 | ||
| 		if ( ! empty( $url ) ) {
 | ||
| 			$filter[] = '`comment_author_url` = %s';
 | ||
| 			$params[] = wp_unslash( $url );
 | ||
| 		}
 | ||
| 		if ( ! empty( $ip ) ) {
 | ||
| 			$filter[] = '`comment_author_IP` = %s';
 | ||
| 			$params[] = wp_unslash( $ip );
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( ! empty( $email ) ) {
 | ||
| 			$filter[] = '`comment_author_email` = %s';
 | ||
| 			$params[] = wp_unslash( $email );
 | ||
| 		}
 | ||
| 		if ( empty( $params ) ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
 | ||
| 		// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
 | ||
| 		$filter_sql = implode( ' OR ', $filter );
 | ||
| 
 | ||
| 		$result = $wpdb->get_var(
 | ||
| 			$wpdb->prepare(
 | ||
| 				sprintf(
 | ||
| 					"SELECT `comment_ID` FROM `$wpdb->comments` WHERE `comment_approved` = 'spam' AND (%s) LIMIT 1",
 | ||
| 					$filter_sql
 | ||
| 				),
 | ||
| 				$params
 | ||
| 			)
 | ||
| 		);
 | ||
| 		// phpcs:enable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
 | ||
| 		// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
 | ||
| 
 | ||
| 		return ! empty( $result );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check for country spam by (anonymized) IP
 | ||
| 	 *
 | ||
| 	 * @since  2.6.9
 | ||
| 	 * @since  2.10.0 Make country check API filterable and use iplocate.io instead of ip2country.info
 | ||
| 	 *
 | ||
| 	 * @param   string $ip IP address.
 | ||
| 	 * @return  boolean    True if the comment is spam based on country filter.
 | ||
| 	 */
 | ||
| 	private static function _is_country_spam( $ip ) {
 | ||
| 		$options = self::get_options();
 | ||
| 
 | ||
| 		$allowed = preg_split(
 | ||
| 			'/[\s,;]+/',
 | ||
| 			$options['country_allowed'],
 | ||
| 			-1,
 | ||
| 			PREG_SPLIT_NO_EMPTY
 | ||
| 		);
 | ||
| 		$denied = preg_split(
 | ||
| 			'/[\s,;]+/',
 | ||
| 			$options['country_denied'],
 | ||
| 			-1,
 | ||
| 			PREG_SPLIT_NO_EMPTY
 | ||
| 		);
 | ||
| 
 | ||
| 		if ( empty( $allowed ) && empty( $denied ) ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		/**
 | ||
| 		 * Filter to hook into the `_is_country_spam` functionality, to implement for example a custom IP check.
 | ||
| 		 *
 | ||
| 		 * @since 2.10.0
 | ||
| 		 *
 | ||
| 		 * @param null   $is_country_spam The `is_country_spam` result.
 | ||
| 		 * @param string $ip              The IP address.
 | ||
| 		 * @param array  $allowed         The list of allowed country codes.
 | ||
| 		 * @param array  $denied          The list of denied country codes.
 | ||
| 		 *
 | ||
| 		 * @return null|boolean The `is_country_spam` result or null.
 | ||
| 		 */
 | ||
| 		$is_country_spam = apply_filters( 'antispam_bee_is_country_spam', null, $ip, $allowed, $denied );
 | ||
| 
 | ||
| 		if ( is_bool( $is_country_spam ) ) {
 | ||
| 			return $is_country_spam;
 | ||
| 		}
 | ||
| 
 | ||
| 		/**
 | ||
| 		 * Filters the IPLocate API key. With this filter, you can add your own IPLocate API key.
 | ||
| 		 *
 | ||
| 		 * @since 2.10.0
 | ||
| 		 *
 | ||
| 		 * @param string  The current IPLocate API key. Default is `null`.
 | ||
| 		 *
 | ||
| 		 * @return string The changed IPLocate API key or null.
 | ||
| 		 */
 | ||
| 		$apikey = apply_filters( 'antispam_bee_country_spam_apikey', '' );
 | ||
| 
 | ||
| 		$response = wp_safe_remote_get(
 | ||
| 			esc_url_raw(
 | ||
| 				sprintf(
 | ||
| 					'https://www.iplocate.io/api/lookup/%s?apikey=%s',
 | ||
| 					self::_anonymize_ip( $ip ),
 | ||
| 					$apikey
 | ||
| 				),
 | ||
| 				'https'
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		if ( is_wp_error( $response ) ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$body = (string) wp_remote_retrieve_body( $response );
 | ||
| 
 | ||
| 		$json = json_decode( $body, true );
 | ||
| 
 | ||
| 		// Check if response is valid json.
 | ||
| 		if ( ! is_array( $json ) ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( empty( $json['country_code'] ) ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$country = strtoupper( $json['country_code'] );
 | ||
| 
 | ||
| 		if ( empty( $country ) || strlen( $country ) !== 2 ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( ! empty( $denied ) ) {
 | ||
| 			return ( in_array( $country, $denied, true ) );
 | ||
| 		}
 | ||
| 
 | ||
| 		return ( ! in_array( $country, $allowed, true ) );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check for BBCode spam
 | ||
| 	 *
 | ||
| 	 * @since   2.5.1
 | ||
| 	 *
 | ||
| 	 * @param   string $body Content of a comment.
 | ||
| 	 * @return  boolean      True for BBCode in content
 | ||
| 	 */
 | ||
| 	private static function _is_bbcode_spam( $body ) {
 | ||
| 		return (bool) preg_match( '/\[url[=\]].*\[\/url\]/is', $body );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check for an already approved e-mail address
 | ||
| 	 *
 | ||
| 	 * @since  2.0
 | ||
| 	 * @since  2.5.1
 | ||
| 	 *
 | ||
| 	 * @param   string $email E-mail address.
 | ||
| 	 * @return  boolean       True for a found entry.
 | ||
| 	 */
 | ||
| 	private static function _is_approved_email( $email ) {
 | ||
| 		global $wpdb;
 | ||
| 
 | ||
| 		$result = $wpdb->get_var(
 | ||
| 			$wpdb->prepare(
 | ||
| 				"SELECT `comment_ID` FROM `$wpdb->comments` WHERE `comment_approved` = '1' AND `comment_author_email` = %s LIMIT 1",
 | ||
| 				wp_unslash( $email )
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		if ( $result ) {
 | ||
| 			return true;
 | ||
| 		}
 | ||
| 
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check for unwanted languages
 | ||
| 	 *
 | ||
| 	 * @since  2.0
 | ||
| 	 * @since  2.6.6
 | ||
| 	 * @since  2.8.2
 | ||
| 	 *
 | ||
| 	 * @param  string $comment_content Content of the comment.
 | ||
| 	 *
 | ||
| 	 * @return boolean TRUE if it is spam.
 | ||
| 	 */
 | ||
| 	private static function _is_lang_spam( $comment_content ) {
 | ||
| 		$allowed_lang = (array) self::get_option( 'translate_lang' );
 | ||
| 
 | ||
| 		$comment_text = wp_strip_all_tags( $comment_content );
 | ||
| 
 | ||
| 		if ( empty( $allowed_lang ) || empty( $comment_text ) ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		/**
 | ||
| 		 * Filters the detected language. With this filter, other detection methods can skip in and detect the language.
 | ||
| 		 *
 | ||
| 		 * @since 2.8.2
 | ||
| 		 *
 | ||
| 		 * @param null   $detected_lang The detected language.
 | ||
| 		 * @param string $comment_text  The text, to detect the language.
 | ||
| 		 *
 | ||
| 		 * @return null|string The detected language or null.
 | ||
| 		 */
 | ||
| 		$detected_lang = apply_filters( 'antispam_bee_detected_lang', null, $comment_text );
 | ||
| 		if ( null !== $detected_lang ) {
 | ||
| 			return ! in_array( $detected_lang, $allowed_lang, true );
 | ||
| 		}
 | ||
| 
 | ||
| 		$word_count = 0;
 | ||
| 		$text       = trim( preg_replace( "/[\n\r\t ]+/", ' ', $comment_text ), ' ' );
 | ||
| 
 | ||
| 		/*
 | ||
| 		 * translators: If your word count is based on single characters (e.g. East Asian characters),
 | ||
| 		 * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.
 | ||
| 		 * Do not translate into your own language.
 | ||
| 		 */
 | ||
| 		if ( strpos( _x( 'words', 'Word count type. Do not translate!' ), 'characters' ) === 0 && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) { // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
 | ||
| 			preg_match_all( '/./u', $text, $words_array );
 | ||
| 			if ( isset( $words_array[0] ) ) {
 | ||
| 				$word_count = count( $words_array[0] );
 | ||
| 			}
 | ||
| 		} else {
 | ||
| 			$words_array = preg_split( "/[\n\r\t ]+/", $text, -1, PREG_SPLIT_NO_EMPTY );
 | ||
| 			$word_count  = count( $words_array );
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $word_count < 10 ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$response = wp_safe_remote_post(
 | ||
| 			'https://api.pluginkollektiv.org/language/v1/',
 | ||
| 			array( 'body' => wp_json_encode( array( 'body' => $comment_text ) ) )
 | ||
| 		);
 | ||
| 
 | ||
| 		if ( is_wp_error( $response )
 | ||
| 			|| wp_remote_retrieve_response_code( $response ) !== 200 ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$detected_lang = wp_remote_retrieve_body( $response );
 | ||
| 		if ( ! $detected_lang ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$detected_lang = json_decode( $detected_lang );
 | ||
| 		if ( ! $detected_lang || ! isset( $detected_lang->code ) ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		return ! in_array( self::_map_lang_code( $detected_lang->code ), $allowed_lang, true );
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Map franc language codes
 | ||
| 	 *
 | ||
| 	 * @since   2.9.0
 | ||
| 	 *
 | ||
| 	 * @param  string $franc_code The franc code, received from the service.
 | ||
| 	 *
 | ||
| 	 * @return string             Mapped ISO code
 | ||
| 	 */
 | ||
| 	private static function _map_lang_code( $franc_code ) {
 | ||
| 		$codes = array(
 | ||
| 			'zha' => 'za',
 | ||
| 			'zho' => 'zh',
 | ||
| 			'zul' => 'zu',
 | ||
| 			'yid' => 'yi',
 | ||
| 			'yor' => 'yo',
 | ||
| 			'xho' => 'xh',
 | ||
| 			'wln' => 'wa',
 | ||
| 			'wol' => 'wo',
 | ||
| 			'ven' => 've',
 | ||
| 			'vie' => 'vi',
 | ||
| 			'vol' => 'vo',
 | ||
| 			'uig' => 'ug',
 | ||
| 			'ukr' => 'uk',
 | ||
| 			'urd' => 'ur',
 | ||
| 			'uzb' => 'uz',
 | ||
| 			'tah' => 'ty',
 | ||
| 			'tam' => 'ta',
 | ||
| 			'tat' => 'tt',
 | ||
| 			'tel' => 'te',
 | ||
| 			'tgk' => 'tg',
 | ||
| 			'tgl' => 'tl',
 | ||
| 			'tha' => 'th',
 | ||
| 			'tir' => 'ti',
 | ||
| 			'ton' => 'to',
 | ||
| 			'tsn' => 'tn',
 | ||
| 			'tso' => 'ts',
 | ||
| 			'tuk' => 'tk',
 | ||
| 			'tur' => 'tr',
 | ||
| 			'twi' => 'tw',
 | ||
| 			'sag' => 'sg',
 | ||
| 			'san' => 'sa',
 | ||
| 			'sin' => 'si',
 | ||
| 			'slk' => 'sk',
 | ||
| 			'slv' => 'sl',
 | ||
| 			'sme' => 'se',
 | ||
| 			'smo' => 'sm',
 | ||
| 			'sna' => 'sn',
 | ||
| 			'snd' => 'sd',
 | ||
| 			'som' => 'so',
 | ||
| 			'sot' => 'st',
 | ||
| 			'spa' => 'es',
 | ||
| 			'sqi' => 'sq',
 | ||
| 			'srd' => 'sc',
 | ||
| 			'srp' => 'sr',
 | ||
| 			'ssw' => 'ss',
 | ||
| 			'sun' => 'su',
 | ||
| 			'swa' => 'sw',
 | ||
| 			'swe' => 'sv',
 | ||
| 			'roh' => 'rm',
 | ||
| 			'ron' => 'ro',
 | ||
| 			'run' => 'rn',
 | ||
| 			'rus' => 'ru',
 | ||
| 			'que' => 'qu',
 | ||
| 			'pan' => 'pa',
 | ||
| 			'pli' => 'pi',
 | ||
| 			'pol' => 'pl',
 | ||
| 			'por' => 'pt',
 | ||
| 			'pus' => 'ps',
 | ||
| 			'oci' => 'oc',
 | ||
| 			'oji' => 'oj',
 | ||
| 			'ori' => 'or',
 | ||
| 			'orm' => 'om',
 | ||
| 			'oss' => 'os',
 | ||
| 			'nau' => 'na',
 | ||
| 			'nav' => 'nv',
 | ||
| 			'nbl' => 'nr',
 | ||
| 			'nde' => 'nd',
 | ||
| 			'ndo' => 'ng',
 | ||
| 			'nep' => 'ne',
 | ||
| 			'nld' => 'nl',
 | ||
| 			'nno' => 'nn',
 | ||
| 			'nob' => 'nb',
 | ||
| 			'nor' => 'no',
 | ||
| 			'nya' => 'ny',
 | ||
| 			'mah' => 'mh',
 | ||
| 			'mal' => 'ml',
 | ||
| 			'mar' => 'mr',
 | ||
| 			'mkd' => 'mk',
 | ||
| 			'mlg' => 'mg',
 | ||
| 			'mlt' => 'mt',
 | ||
| 			'mon' => 'mn',
 | ||
| 			'mri' => 'mi',
 | ||
| 			'msa' => 'ms',
 | ||
| 			'mya' => 'my',
 | ||
| 			'lao' => 'lo',
 | ||
| 			'lat' => 'la',
 | ||
| 			'lav' => 'lv',
 | ||
| 			'lim' => 'li',
 | ||
| 			'lin' => 'ln',
 | ||
| 			'lit' => 'lt',
 | ||
| 			'ltz' => 'lb',
 | ||
| 			'lub' => 'lu',
 | ||
| 			'lug' => 'lg',
 | ||
| 			'kal' => 'kl',
 | ||
| 			'kan' => 'kn',
 | ||
| 			'kas' => 'ks',
 | ||
| 			'kat' => 'ka',
 | ||
| 			'kau' => 'kr',
 | ||
| 			'kaz' => 'kk',
 | ||
| 			'khm' => 'km',
 | ||
| 			'kik' => 'ki',
 | ||
| 			'kin' => 'rw',
 | ||
| 			'kir' => 'ky',
 | ||
| 			'kom' => 'kv',
 | ||
| 			'kon' => 'kg',
 | ||
| 			'kor' => 'ko',
 | ||
| 			'kua' => 'kj',
 | ||
| 			'kur' => 'ku',
 | ||
| 			'jav' => 'jv',
 | ||
| 			'jpn' => 'ja',
 | ||
| 			'ibo' => 'ig',
 | ||
| 			'ido' => 'io',
 | ||
| 			'iii' => 'ii',
 | ||
| 			'iku' => 'iu',
 | ||
| 			'ile' => 'ie',
 | ||
| 			'ina' => 'ia',
 | ||
| 			'ind' => 'id',
 | ||
| 			'ipk' => 'ik',
 | ||
| 			'isl' => 'is',
 | ||
| 			'ita' => 'it',
 | ||
| 			'hat' => 'ht',
 | ||
| 			'hau' => 'ha',
 | ||
| 			'hbs' => 'sh',
 | ||
| 			'heb' => 'he',
 | ||
| 			'her' => 'hz',
 | ||
| 			'hin' => 'hi',
 | ||
| 			'hmo' => 'ho',
 | ||
| 			'hrv' => 'hr',
 | ||
| 			'hun' => 'hu',
 | ||
| 			'hye' => 'hy',
 | ||
| 			'gla' => 'gd',
 | ||
| 			'gle' => 'ga',
 | ||
| 			'glg' => 'gl',
 | ||
| 			'glv' => 'gv',
 | ||
| 			'grn' => 'gn',
 | ||
| 			'guj' => 'gu',
 | ||
| 			'fao' => 'fo',
 | ||
| 			'fas' => 'fa',
 | ||
| 			'fij' => 'fj',
 | ||
| 			'fin' => 'fi',
 | ||
| 			'fra' => 'fr',
 | ||
| 			'fry' => 'fy',
 | ||
| 			'ful' => 'ff',
 | ||
| 			'ell' => 'el',
 | ||
| 			'eng' => 'en',
 | ||
| 			'epo' => 'eo',
 | ||
| 			'est' => 'et',
 | ||
| 			'eus' => 'eu',
 | ||
| 			'ewe' => 'ee',
 | ||
| 			'dan' => 'da',
 | ||
| 			'deu' => 'de',
 | ||
| 			'div' => 'dv',
 | ||
| 			'dzo' => 'dz',
 | ||
| 			'cat' => 'ca',
 | ||
| 			'ces' => 'cs',
 | ||
| 			'cha' => 'ch',
 | ||
| 			'che' => 'ce',
 | ||
| 			'chu' => 'cu',
 | ||
| 			'chv' => 'cv',
 | ||
| 			'cor' => 'kw',
 | ||
| 			'cos' => 'co',
 | ||
| 			'cre' => 'cr',
 | ||
| 			'cym' => 'cy',
 | ||
| 			'bak' => 'ba',
 | ||
| 			'bam' => 'bm',
 | ||
| 			'bel' => 'be',
 | ||
| 			'ben' => 'bn',
 | ||
| 			'bis' => 'bi',
 | ||
| 			'bod' => 'bo',
 | ||
| 			'bos' => 'bs',
 | ||
| 			'bre' => 'br',
 | ||
| 			'bul' => 'bg',
 | ||
| 			'aar' => 'aa',
 | ||
| 			'abk' => 'ab',
 | ||
| 			'afr' => 'af',
 | ||
| 			'aka' => 'ak',
 | ||
| 			'amh' => 'am',
 | ||
| 			'ara' => 'ar',
 | ||
| 			'arg' => 'an',
 | ||
| 			'asm' => 'as',
 | ||
| 			'ava' => 'av',
 | ||
| 			'ave' => 'ae',
 | ||
| 			'aym' => 'ay',
 | ||
| 			'aze' => 'az',
 | ||
| 			'nds' => 'de',
 | ||
| 		);
 | ||
| 
 | ||
| 		if ( array_key_exists( $franc_code, $codes ) ) {
 | ||
| 			return $codes[ $franc_code ];
 | ||
| 		}
 | ||
| 
 | ||
| 		return $franc_code;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Trim IP addresses
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.5.1
 | ||
| 	 *
 | ||
| 	 * @param   string  $ip       Original IP.
 | ||
| 	 * @param   boolean $cut_end  Shortening the end.
 | ||
| 	 * @return  string            Shortened IP.
 | ||
| 	 */
 | ||
| 	private static function _cut_ip( $ip, $cut_end = true ) {
 | ||
| 		$separator = ( self::_is_ipv4( $ip ) ? '.' : ':' );
 | ||
| 
 | ||
| 		return str_replace(
 | ||
| 			( $cut_end ? strrchr( $ip, $separator ) : strstr( $ip, $separator ) ),
 | ||
| 			'',
 | ||
| 			$ip
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Anonymize the IP addresses
 | ||
| 	 *
 | ||
| 	 * @since   2.5.1
 | ||
| 	 *
 | ||
| 	 * @param   string $ip Original IP.
 | ||
| 	 * @return  string     Anonymous IP.
 | ||
| 	 */
 | ||
| 	private static function _anonymize_ip( $ip ) {
 | ||
| 		if ( self::_is_ipv4( $ip ) ) {
 | ||
| 			return self::_cut_ip( $ip ) . '.0';
 | ||
| 		}
 | ||
| 
 | ||
| 		return self::_cut_ip( $ip, false ) . ':0:0:0:0:0:0:0';
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Rotates the IP address
 | ||
| 	 *
 | ||
| 	 * @since   2.4.5
 | ||
| 	 *
 | ||
| 	 * @param   string $ip  IP address.
 | ||
| 	 * @return  string      Turned IP address.
 | ||
| 	 */
 | ||
| 	private static function _reverse_ip( $ip ) {
 | ||
| 		return implode(
 | ||
| 			'.',
 | ||
| 			array_reverse(
 | ||
| 				explode(
 | ||
| 					'.',
 | ||
| 					$ip
 | ||
| 				)
 | ||
| 			)
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check for an IPv4 address
 | ||
| 	 *
 | ||
| 	 * @since  2.4
 | ||
| 	 * @since  2.6.4
 | ||
| 	 *
 | ||
| 	 * @param   string $ip  IP to validate.
 | ||
| 	 * @return  integer       TRUE if IPv4.
 | ||
| 	 */
 | ||
| 	private static function _is_ipv4( $ip ) {
 | ||
| 		if ( function_exists( 'filter_var' ) ) {
 | ||
| 			return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) !== false;
 | ||
| 		} else {
 | ||
| 			return preg_match( '/^\d{1,3}(\.\d{1,3}){3}$/', $ip );
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Check for an IPv6 address
 | ||
| 	 *
 | ||
| 	 * @since  2.6.2
 | ||
| 	 * @since  2.6.4
 | ||
| 	 *
 | ||
| 	 * @param   string $ip  IP to validate.
 | ||
| 	 * @return  boolean       TRUE if IPv6.
 | ||
| 	 */
 | ||
| 	private static function _is_ipv6( $ip ) {
 | ||
| 		if ( function_exists( 'filter_var' ) ) {
 | ||
| 			return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) !== false;
 | ||
| 		} else {
 | ||
| 			return ! self::_is_ipv4( $ip );
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Testing on mobile devices
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 *
 | ||
| 	 * @return  boolean  TRUE if "wptouch" is active
 | ||
| 	 */
 | ||
| 	private static function _is_mobile() {
 | ||
| 		return strpos( get_template_directory(), 'wptouch' );
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Testing if we are on an AMP site.
 | ||
| 	 *
 | ||
| 	 * Starting with v2.0, amp_is_request() is the preferred method to check,
 | ||
| 	 * but we fall back to the then deprecated is_amp_endpoint() as needed.
 | ||
| 	 *
 | ||
| 	 * @return bool
 | ||
| 	 */
 | ||
| 	private static function _is_amp() {
 | ||
| 		return ( function_exists( 'amp_is_request' ) && amp_is_request() ) || ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 	/*
 | ||
| 	*   ############################
 | ||
| 	*   #####  SPAM-TREATMENT  #####
 | ||
| 	*   ############################
 | ||
| 	*/
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Execution of the delete/marking process
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.6.0
 | ||
| 	 *
 | ||
| 	 * @param   array   $comment  Untreated commentary data.
 | ||
| 	 * @param   string  $reason   Reason for suspicion.
 | ||
| 	 * @param   boolean $is_ping  Ping (optional).
 | ||
| 	 * @return  array   $comment  Treated commentary data.
 | ||
| 	 */
 | ||
| 	private static function _handle_spam_request( $comment, $reason, $is_ping = false ) {
 | ||
| 
 | ||
| 		$options = self::get_options();
 | ||
| 
 | ||
| 		$spam_remove = ! $options['flag_spam'];
 | ||
| 		$spam_notice = ! $options['no_notice'];
 | ||
| 
 | ||
| 		// Filter settings.
 | ||
| 		$ignore_filter = $options['ignore_filter'];
 | ||
| 		$ignore_type   = $options['ignore_type'];
 | ||
| 		$ignore_reason = in_array( $reason, (array) $options['ignore_reasons'], true );
 | ||
| 
 | ||
| 		// Remember spam.
 | ||
| 		self::_update_spam_log( $comment );
 | ||
| 		self::_update_spam_count();
 | ||
| 		self::_update_daily_stats();
 | ||
| 
 | ||
| 		// Delete spam.
 | ||
| 		if ( $spam_remove ) {
 | ||
| 			self::_go_in_peace();
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $ignore_filter && ( ( 1 === (int) $ignore_type && $is_ping ) || ( 2 === (int) $ignore_type && ! $is_ping ) ) ) {
 | ||
| 			self::_go_in_peace();
 | ||
| 		}
 | ||
| 
 | ||
| 		// Spam reason.
 | ||
| 		if ( $ignore_reason ) {
 | ||
| 			self::_go_in_peace();
 | ||
| 		}
 | ||
| 		self::$_reason = $reason;
 | ||
| 
 | ||
| 		// Mark spam.
 | ||
| 		add_filter(
 | ||
| 			'pre_comment_approved',
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'return_spam',
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		// Send e-mail.
 | ||
| 		add_action(
 | ||
| 			'comment_post',
 | ||
| 			array(
 | ||
| 				__CLASS__,
 | ||
| 				'send_mail_notification',
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		// Spam reason as comment meta.
 | ||
| 		if ( $spam_notice ) {
 | ||
| 			add_action(
 | ||
| 				'comment_post',
 | ||
| 				array(
 | ||
| 					__CLASS__,
 | ||
| 					'add_spam_reason_to_comment',
 | ||
| 				)
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		return $comment;
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Logfile with detected spam
 | ||
| 	 *
 | ||
| 	 * @since  2.5.7
 | ||
| 	 * @since  2.6.1
 | ||
| 	 *
 | ||
| 	 * @param   array $comment Array with commentary data.
 | ||
| 	 * @return  mixed        FALSE in case of error
 | ||
| 	 */
 | ||
| 	private static function _update_spam_log( $comment ) {
 | ||
| 		if ( ! defined( 'ANTISPAM_BEE_LOG_FILE' ) || ! ANTISPAM_BEE_LOG_FILE || ! is_writable( ANTISPAM_BEE_LOG_FILE ) || validate_file( ANTISPAM_BEE_LOG_FILE ) === 1 ) {
 | ||
| 			return false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$entry = sprintf(
 | ||
| 			'%s comment for post=%d from host=%s marked as spam%s',
 | ||
| 			current_time( 'mysql' ),
 | ||
| 			$comment['comment_post_ID'],
 | ||
| 			$comment['comment_author_IP'],
 | ||
| 			PHP_EOL
 | ||
| 		);
 | ||
| 
 | ||
| 		file_put_contents(
 | ||
| 			ANTISPAM_BEE_LOG_FILE,
 | ||
| 			$entry,
 | ||
| 			FILE_APPEND | LOCK_EX
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Sends the 403 header and terminates the connection
 | ||
| 	 *
 | ||
| 	 * @since   2.5.6
 | ||
| 	 */
 | ||
| 	private static function _go_in_peace() {
 | ||
| 		status_header( 403 );
 | ||
| 		die( 'Spam deleted.' );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Return real client IP
 | ||
| 	 *
 | ||
| 	 * @since   2.6.1
 | ||
| 	 * @since   2.11.4 Only use `REMOTE_ADDR` to get the IP, make it filterable with `pre_comment_user_ip`
 | ||
| 	 * @since   2.11.5 Switch to own filter `antispam_bee_trusted_ip`
 | ||
| 	 *
 | ||
| 	 * @hook    string  pre_comment_user_ip  The Client IP
 | ||
| 	 *
 | ||
| 	 * @return  string  $ip  Client IP
 | ||
| 	 */
 | ||
| 	public static function get_client_ip() {
 | ||
| 		/**
 | ||
| 		 * Hook for allowing to modify the client IP used by Antispam Bee. Default value is the `REMOTE_ADDR`.
 | ||
| 		 * IMPORTANT: Don’t return an empty string here, otherwise all comments are marked as spam.
 | ||
| 		 *
 | ||
| 		 * @link https://developer.wordpress.org/reference/hooks/pre_comment_user_ip/
 | ||
| 		 *
 | ||
| 		 * @return string
 | ||
| 		 */
 | ||
| 		// phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
 | ||
| 		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated
 | ||
| 		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
 | ||
| 		return self::_sanitize_ip( (string) apply_filters( 'antispam_bee_trusted_ip', wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) );
 | ||
| 		// phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
 | ||
| 		// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotValidated
 | ||
| 		// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Sanitize an IP string.
 | ||
| 	 *
 | ||
| 	 * @param string $raw_ip The raw IP.
 | ||
| 	 *
 | ||
| 	 * @return string The sanitized IP or an empty string.
 | ||
| 	 */
 | ||
| 	private static function _sanitize_ip( $raw_ip ) {
 | ||
| 
 | ||
| 		if ( strpos( $raw_ip, ',' ) !== false ) {
 | ||
| 			$ips    = explode( ',', $raw_ip );
 | ||
| 			$raw_ip = trim( $ips[0] );
 | ||
| 		}
 | ||
| 		if ( function_exists( 'filter_var' ) ) {
 | ||
| 			return (string) filter_var(
 | ||
| 				$raw_ip,
 | ||
| 				FILTER_VALIDATE_IP
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		return (string) preg_replace(
 | ||
| 			'/[^0-9a-f:. ]/si',
 | ||
| 			'',
 | ||
| 			$raw_ip
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Add spam reason as comment data
 | ||
| 	 *
 | ||
| 	 * @since   2.6.0
 | ||
| 	 *
 | ||
| 	 * @param   integer $comment_id  Comment ID.
 | ||
| 	 */
 | ||
| 	public static function add_spam_reason_to_comment( $comment_id ) {
 | ||
| 		add_comment_meta(
 | ||
| 			$comment_id,
 | ||
| 			'antispam_bee_reason',
 | ||
| 			self::$_reason
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Delete spam reason as comment data
 | ||
| 	 *
 | ||
| 	 * @since   2.6.0
 | ||
| 	 *
 | ||
| 	 * @param   integer $comment_id  Comment ID.
 | ||
| 	 */
 | ||
| 	public static function delete_spam_reason_by_comment( $comment_id ) {
 | ||
| 		delete_comment_meta(
 | ||
| 			$comment_id,
 | ||
| 			'antispam_bee_reason'
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Updates the Antispam Bee reason for manual transitions
 | ||
| 	 *
 | ||
| 	 * @since   2.9.2
 | ||
| 	 * @param  WP_Comment $comment Comment Object.
 | ||
| 	 */
 | ||
| 	public static function update_antispam_bee_reason( $comment ) {
 | ||
| 		update_comment_meta( $comment->comment_ID, 'antispam_bee_reason', 'manually' );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Get the current post ID.
 | ||
| 	 *
 | ||
| 	 * @since   2.7.1
 | ||
| 	 */
 | ||
| 	public static function populate_post_id() {
 | ||
| 
 | ||
| 		if ( null === self::$_current_post_id ) {
 | ||
| 			self::$_current_post_id = get_the_ID();
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Send notification via e-mail
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.5.7
 | ||
| 	 * @since  2.10.0 Change plugin website URL
 | ||
| 	 *
 | ||
| 	 * @hook    string  antispam_bee_notification_subject  Custom subject for notification mails
 | ||
| 	 *
 | ||
| 	 * @param   int $id  ID of the comment.
 | ||
| 	 * @return  int  $id  ID of the comment.
 | ||
| 	 */
 | ||
| 	public static function send_mail_notification( $id ) {
 | ||
| 		$options = self::get_options();
 | ||
| 
 | ||
| 		if ( ! $options['email_notify'] ) {
 | ||
| 			return $id;
 | ||
| 		}
 | ||
| 
 | ||
| 		$comment = get_comment( $id, ARRAY_A );
 | ||
| 
 | ||
| 		if ( empty( $comment ) ) {
 | ||
| 			return $id;
 | ||
| 		}
 | ||
| 
 | ||
| 		$post = get_post( $comment['comment_post_ID'] );
 | ||
| 		if ( ! $post ) {
 | ||
| 			return $id;
 | ||
| 		}
 | ||
| 
 | ||
| 		self::load_plugin_lang();
 | ||
| 		self::add_reasons_to_defaults();
 | ||
| 
 | ||
| 		$subject = sprintf(
 | ||
| 			'[%s] %s',
 | ||
| 			stripslashes_deep(
 | ||
| 				// phpcs:ignore PHPCompatibility.ParameterValues.NewHTMLEntitiesEncodingDefault.NotSet
 | ||
| 				html_entity_decode(
 | ||
| 					get_bloginfo( 'name' ),
 | ||
| 					ENT_QUOTES
 | ||
| 				)
 | ||
| 			),
 | ||
| 			esc_html__( 'Comment marked as spam', 'antispam-bee' )
 | ||
| 		);
 | ||
| 
 | ||
| 		// Content.
 | ||
| 		$content = strip_tags( stripslashes( $comment['comment_content'] ) );
 | ||
| 		if ( ! $content ) {
 | ||
| 			$content = sprintf(
 | ||
| 				'-- %s --',
 | ||
| 				esc_html__( 'Content removed by Antispam Bee', 'antispam-bee' )
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		// Prepare Comment Type.
 | ||
| 		$comment_name = __( 'Comment', 'antispam-bee' );
 | ||
| 		if ( 'trackback' === $comment['comment_type'] ) {
 | ||
| 			$comment_name = __( 'Trackback', 'antispam-bee' );
 | ||
| 		}
 | ||
| 		if ( 'pingback' === $comment['comment_type'] ) {
 | ||
| 			$comment_name = __( 'Pingback', 'antispam-bee' );
 | ||
| 		}
 | ||
| 
 | ||
| 		// Body.
 | ||
| 		$body = sprintf(
 | ||
| 			"%s \"%s\"\r\n\r\n",
 | ||
| 			esc_html__( 'New spam comment on your post', 'antispam-bee' ),
 | ||
| 			strip_tags( $post->post_title )
 | ||
| 		) . sprintf(
 | ||
| 			"%s: %s\r\n",
 | ||
| 			esc_html__( 'Author', 'antispam-bee' ),
 | ||
| 			( empty( $comment['comment_author'] ) ? '' : strip_tags( $comment['comment_author'] ) )
 | ||
| 		) . sprintf(
 | ||
| 			"URL: %s\r\n",
 | ||
| 			// empty check exists.
 | ||
| 			esc_url( $comment['comment_author_url'] )
 | ||
| 		) . sprintf(
 | ||
| 			"%s: %s\r\n",
 | ||
| 			esc_html__( 'Type', 'antispam-bee' ),
 | ||
| 			esc_html( $comment_name )
 | ||
| 		) . sprintf(
 | ||
| 			"Whois: http://whois.arin.net/rest/ip/%s\r\n",
 | ||
| 			$comment['comment_author_IP']
 | ||
| 		) . sprintf(
 | ||
| 			"%s: %s\r\n\r\n",
 | ||
| 			esc_html__( 'Spam Reason', 'antispam-bee' ),
 | ||
| 			esc_html( self::$defaults['reasons'][ self::$_reason ] )
 | ||
| 		) . sprintf(
 | ||
| 			"%s\r\n\r\n\r\n",
 | ||
| 			$content
 | ||
| 		) . (
 | ||
| 			EMPTY_TRASH_DAYS ? (
 | ||
| 				sprintf(
 | ||
| 					"%s: %s\r\n",
 | ||
| 					esc_html__( 'Trash it', 'antispam-bee' ),
 | ||
| 					admin_url( 'comment.php?action=trash&c=' . $id )
 | ||
| 				)
 | ||
| 			) : (
 | ||
| 				sprintf(
 | ||
| 					"%s: %s\r\n",
 | ||
| 					esc_html__( 'Delete it', 'antispam-bee' ),
 | ||
| 					admin_url( 'comment.php?action=delete&c=' . $id )
 | ||
| 				)
 | ||
| 			)
 | ||
| 		) . sprintf(
 | ||
| 			"%s: %s\r\n",
 | ||
| 			esc_html__( 'Approve it', 'antispam-bee' ),
 | ||
| 			admin_url( 'comment.php?action=approve&c=' . $id )
 | ||
| 		) . sprintf(
 | ||
| 			"%s: %s\r\n\r\n",
 | ||
| 			esc_html__( 'Spam list', 'antispam-bee' ),
 | ||
| 			admin_url( 'edit-comments.php?comment_status=spam' )
 | ||
| 		) . sprintf(
 | ||
| 			"%s\r\n%s\r\n",
 | ||
| 			esc_html__( 'Notify message by Antispam Bee', 'antispam-bee' ),
 | ||
| 			esc_html__( 'https://antispambee.pluginkollektiv.org/', 'antispam-bee' )
 | ||
| 		);
 | ||
| 
 | ||
| 		wp_mail(
 | ||
| 			/**
 | ||
| 			 * Filters the recipients of the spam notification.
 | ||
| 			 *
 | ||
| 			 * @param array The recipients array.
 | ||
| 			 */
 | ||
| 			apply_filters(
 | ||
| 				'antispam_bee_notification_recipients',
 | ||
| 				array( get_bloginfo( 'admin_email' ) )
 | ||
| 			),
 | ||
| 			/**
 | ||
| 			 * Filters the subject of the spam notification.
 | ||
| 			 *
 | ||
| 			 * @param string $subject subject line.
 | ||
| 			 */
 | ||
| 			apply_filters(
 | ||
| 				'antispam_bee_notification_subject',
 | ||
| 				$subject
 | ||
| 			),
 | ||
| 			$body
 | ||
| 		);
 | ||
| 
 | ||
| 		return $id;
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 	/*
 | ||
| 	*   ############################
 | ||
| 	*   #######  STATISTICS  #######
 | ||
| 	*   ############################
 | ||
| 	*/
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Return the number of spam comments
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 */
 | ||
| 	private static function _get_spam_count() {
 | ||
| 		// Init.
 | ||
| 		$count = intval( self::get_option( 'spam_count' ) );
 | ||
| 
 | ||
| 		// Fire.
 | ||
| 		return ( get_locale() === 'de_DE' ? number_format( $count, 0, '', '.' ) : number_format_i18n( $count ) );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Output the number of spam comments
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.4
 | ||
| 	 */
 | ||
| 	public static function the_spam_count() {
 | ||
| 		echo esc_html( self::_get_spam_count() );
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Update the number of spam comments
 | ||
| 	 *
 | ||
| 	 * @since  0.1
 | ||
| 	 * @since  2.6.1
 | ||
| 	 */
 | ||
| 	private static function _update_spam_count() {
 | ||
| 		// Skip if not enabled.
 | ||
| 		if ( ! self::get_option( 'dashboard_count' ) ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		self::_update_option(
 | ||
| 			'spam_count',
 | ||
| 			intval( self::get_option( 'spam_count' ) + 1 )
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Update statistics
 | ||
| 	 *
 | ||
| 	 * @since  1.9
 | ||
| 	 * @since  2.6.1
 | ||
| 	 */
 | ||
| 	private static function _update_daily_stats() {
 | ||
| 		// Skip if not enabled.
 | ||
| 		if ( ! self::get_option( 'dashboard_chart' ) ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		// Init.
 | ||
| 		$stats = (array) self::get_option( 'daily_stats' );
 | ||
| 		$today = (int) strtotime( 'today' );
 | ||
| 
 | ||
| 		// Count up.
 | ||
| 		if ( array_key_exists( $today, $stats ) ) {
 | ||
| 			$stats[ $today ] ++;
 | ||
| 		} else {
 | ||
| 			$stats[ $today ] = 1;
 | ||
| 		}
 | ||
| 
 | ||
| 		// Sort.
 | ||
| 		krsort( $stats, SORT_NUMERIC );
 | ||
| 
 | ||
| 		// Save.
 | ||
| 		self::_update_option(
 | ||
| 			'daily_stats',
 | ||
| 			array_slice( $stats, 0, 31, true )
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Returns the secret of a post used in the textarea name attribute.
 | ||
| 	 *
 | ||
| 	 * @since 2.10.0 Modify secret generation because `always_allowed` option not longer exists
 | ||
| 	 *
 | ||
| 	 * @param int $post_id The Post ID.
 | ||
| 	 *
 | ||
| 	 * @return string
 | ||
| 	 */
 | ||
| 	public static function get_secret_name_for_post( $post_id ) {
 | ||
| 		$secret = substr( sha1( md5( 'comment-id' . self::$_salt ) ), 0, 10 );
 | ||
| 
 | ||
| 		$secret = self::ensure_secret_starts_with_letter( $secret );
 | ||
| 
 | ||
| 		/**
 | ||
| 		 * Filters the secret for a post, which is used in the textarea name attribute.
 | ||
| 		 *
 | ||
| 		 * @param string $secret The secret.
 | ||
| 		 * @param int    $post_id The post ID.
 | ||
| 		 * @param bool   $always_allowed Whether the comment form is used outside of the single post view or not.
 | ||
| 		 */
 | ||
| 		return apply_filters(
 | ||
| 			'ab_get_secret_name_for_post',
 | ||
| 			$secret,
 | ||
| 			(int) $post_id,
 | ||
| 			(bool) self::get_option( 'always_allowed' )
 | ||
| 		);
 | ||
| 
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Returns the secret of a post used in the textarea id attribute.
 | ||
| 	 *
 | ||
| 	 * @since 2.10.0 Modify secret generation because `always_allowed` option not longer exists
 | ||
| 	 *
 | ||
| 	 * @param int $post_id The post ID.
 | ||
| 	 *
 | ||
| 	 * @return string
 | ||
| 	 */
 | ||
| 	public static function get_secret_id_for_post( $post_id ) {
 | ||
| 
 | ||
| 		$secret = substr( sha1( md5( 'comment-id' . self::$_salt ) ), 0, 10 );
 | ||
| 
 | ||
| 		$secret = self::ensure_secret_starts_with_letter( $secret );
 | ||
| 
 | ||
| 		/**
 | ||
| 		 * Filters the secret for a post, which is used in the textarea id attribute.
 | ||
| 		 *
 | ||
| 		 * @param string $secret The secret.
 | ||
| 		 * @param int    $post_id The post ID.
 | ||
| 		 * @param bool   $always_allowed Whether the comment form is used outside of the single post view or not.
 | ||
| 		 */
 | ||
| 		return apply_filters(
 | ||
| 			'ab_get_secret_id_for_post',
 | ||
| 			$secret,
 | ||
| 			(int) $post_id,
 | ||
| 			(bool) self::get_option( 'always_allowed' )
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Ensures that the secret starts with a letter.
 | ||
| 	 *
 | ||
| 	 * @param string $secret The secret.
 | ||
| 	 *
 | ||
| 	 * @return string
 | ||
| 	 */
 | ||
| 	public static function ensure_secret_starts_with_letter( $secret ) {
 | ||
| 
 | ||
| 		$first_char = substr( $secret, 0, 1 );
 | ||
| 		if ( is_numeric( $first_char ) ) {
 | ||
| 			return chr( $first_char + 97 ) . substr( $secret, 1 );
 | ||
| 		} else {
 | ||
| 			return $secret;
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Returns 'spam'
 | ||
| 	 *
 | ||
| 	 * @since 2.7.3
 | ||
| 	 *
 | ||
| 	 * @return string
 | ||
| 	 */
 | ||
| 	public static function return_spam() {
 | ||
| 
 | ||
| 		return 'spam';
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * A wrapper around wp_parse_url().
 | ||
| 	 *
 | ||
| 	 * @since 2.8.2
 | ||
| 	 *
 | ||
| 	 * @param string $url The URL to parse.
 | ||
| 	 * @param string $component The component to get back.
 | ||
| 	 *
 | ||
| 	 * @return string
 | ||
| 	 */
 | ||
| 	private static function parse_url( $url, $component = 'host' ) {
 | ||
| 
 | ||
| 		$parts = wp_parse_url( $url );
 | ||
| 		return ( is_array( $parts ) && isset( $parts[ $component ] ) ) ? $parts[ $component ] : '';
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Updates the database structure if necessary
 | ||
| 	 *
 | ||
| 	 * @since 2.10.0 Add update routine for country option names
 | ||
| 	 */
 | ||
| 	public static function update_database() {
 | ||
| 		if ( self::db_version_is_current() ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		$version_from_db = floatval( get_option( 'antispambee_db_version', 0 ) );
 | ||
| 		if ( $version_from_db < 1.01 ) {
 | ||
| 			global $wpdb;
 | ||
| 
 | ||
| 			/**
 | ||
| 			 * In Version 2.9 the IP of the commenter was saved as a hash. We reverted this solution.
 | ||
| 			 * Therefore, we need to delete this unused data.
 | ||
| 			 */
 | ||
| 			//phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
 | ||
| 			$sql = 'delete from `' . $wpdb->commentmeta . '` where `meta_key` IN ("antispam_bee_iphash")';
 | ||
| 			$wpdb->query( $sql );
 | ||
| 			//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
 | ||
| 		}
 | ||
| 
 | ||
| 		// DB version was raised in ASB 2.10.0 to 1.02.
 | ||
| 		if ( $version_from_db < 1.02 ) {
 | ||
| 			// Update option names.
 | ||
| 			$options = self::get_options();
 | ||
| 			if ( isset( $options['country_black'] ) ) {
 | ||
| 				$options['country_denied'] = $options['country_black'];
 | ||
| 				unset( $options['country_black'] );
 | ||
| 			}
 | ||
| 			if ( isset( $options['country_white'] ) ) {
 | ||
| 				$options['country_allowed'] = $options['country_white'];
 | ||
| 				unset( $options['country_white'] );
 | ||
| 			}
 | ||
| 
 | ||
| 			update_option(
 | ||
| 				'antispam_bee',
 | ||
| 				$options
 | ||
| 			);
 | ||
| 
 | ||
| 			wp_cache_set(
 | ||
| 				'antispam_bee',
 | ||
| 				$options
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		update_option( 'antispambee_db_version', self::$db_version );
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Whether the database structure is up to date.
 | ||
| 	 *
 | ||
| 	 * @since  2.10.0 Return a float instead of int
 | ||
| 	 *
 | ||
| 	 * @return bool
 | ||
| 	 */
 | ||
| 	private static function db_version_is_current() {
 | ||
| 
 | ||
| 		$current_version = floatval( get_option( 'antispambee_db_version', 0 ) );
 | ||
| 		return $current_version === self::$db_version;
 | ||
| 
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Runs after upgrades are completed.
 | ||
| 	 *
 | ||
| 	 * @since 2.10.0
 | ||
| 	 *
 | ||
| 	 * @param \WP_Upgrader $wp_upgrader WP_Upgrader instance.
 | ||
| 	 * @param array        $hook_extra Array of bulk item update data.
 | ||
| 	 */
 | ||
| 	public static function upgrades_completed( $wp_upgrader, $hook_extra ) {
 | ||
| 		if ( ! $wp_upgrader instanceof Plugin_Upgrader || ! isset( $hook_extra['plugins'] ) ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		$updated_plugins = $hook_extra['plugins'];
 | ||
| 		$asb_updated = false;
 | ||
| 		foreach ( $updated_plugins as $updated_plugin ) {
 | ||
| 			if ( $updated_plugin !== self::$_base ) {
 | ||
| 				continue;
 | ||
| 			}
 | ||
| 			$asb_updated = true;
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( false === $asb_updated ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		self::asb_updated();
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Runs after an upgrade via an uploaded ZIP package was completed.
 | ||
| 	 *
 | ||
| 	 * @since 2.10.0
 | ||
| 	 *
 | ||
| 	 * @param string $package The package file.
 | ||
| 	 * @param array  $data The new plugin or theme data.
 | ||
| 	 * @param string $package_type The package type.
 | ||
| 	 */
 | ||
| 	public static function uploaded_upgrade_completed( $package, $data, $package_type ) {
 | ||
| 		if ( 'plugin' !== $package_type ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		$text_domain = isset( $data['TextDomain'] ) ? $data['TextDomain'] : '';
 | ||
| 
 | ||
| 		if ( 'antispam-bee' !== $text_domain ) {
 | ||
| 			return;
 | ||
| 		}
 | ||
| 
 | ||
| 		self::asb_updated();
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Runs after ASB was updated.
 | ||
| 	 *
 | ||
| 	 * @since 2.10.0
 | ||
| 	 *
 | ||
| 	 * @return void
 | ||
| 	 */
 | ||
| 	private static function asb_updated() {
 | ||
| 		self::update_database();
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| // Fire.
 | ||
| add_action(
 | ||
| 	'plugins_loaded',
 | ||
| 	array(
 | ||
| 		'Antispam_Bee',
 | ||
| 		'init',
 | ||
| 	)
 | ||
| );
 | ||
| 
 | ||
| // Activation.
 | ||
| register_activation_hook(
 | ||
| 	__FILE__,
 | ||
| 	array(
 | ||
| 		'Antispam_Bee',
 | ||
| 		'activate',
 | ||
| 	)
 | ||
| );
 | ||
| 
 | ||
| // Deactivation.
 | ||
| register_deactivation_hook(
 | ||
| 	__FILE__,
 | ||
| 	array(
 | ||
| 		'Antispam_Bee',
 | ||
| 		'deactivate',
 | ||
| 	)
 | ||
| );
 | ||
| 
 | ||
| // Uninstall.
 | ||
| register_uninstall_hook(
 | ||
| 	__FILE__,
 | ||
| 	array(
 | ||
| 		'Antispam_Bee',
 | ||
| 		'uninstall',
 | ||
| 	)
 | ||
| );
 | ||
| 
 | ||
| // Upgrade notice.
 | ||
| add_action(
 | ||
| 	'in_plugin_update_message-' . __FILE__,
 | ||
| 	array(
 | ||
| 		'Antispam_Bee',
 | ||
| 		'upgrade_notice',
 | ||
| 	)
 | ||
| );
 |