HEX
Server: Apache
System: Linux vps.teamads.com 4.18.0-553.126.1.el8_10.x86_64 #1 SMP Thu May 28 06:44:09 EDT 2026 x86_64
User: teamadsc (1024)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: /home/teamadsc/public_html/wp-content/plugins/wp-defender/src/model/setting/class-captcha.php
<?php
/**
 * Handle Captcha settings.
 *
 * @package WP_Defender\Model\Setting
 */

namespace WP_Defender\Model\Setting;

use Calotes\Model\Setting;

/**
 * Model for Captcha settings.
 */
class Captcha extends Setting {
	/**
	 * Turnstile provider key.
	 */
	const TURNSTILE = 'turnstile';

	/**
	 * Option name.
	 * We'll keep the current table for all Captcha for backward compatibility. There's no need to edit the table name to store settings in the database.
	 *
	 * @var string
	 */
	protected $table = 'wd_recaptcha_settings';

	/**
	 * Feature status.
	 *
	 * @var bool
	 * @defender_property
	 */
	public $enabled = false;

	/**
	 * Captcha Provider.
	 *
	 * @var string
	 * @defender_property
	 */
	public $provider = 'recaptcha';

	/**
	 * Active Captcha type.
	 *
	 * @var string
	 * @defender_property
	 * @rule required
	 * @rule in[v2_checkbox,v2_invisible,v3_recaptcha,turnstile]
	 */
	public $active_type = 'v2_checkbox';

	/**
	 * Data for v2 checkbox reCaptcha.
	 *
	 * @var array
	 * @defender_property
	 */
	public $data_v2_checkbox;

	/**
	 * Data for v2 invisible reCaptcha.
	 *
	 * @var array
	 * @defender_property
	 */
	public $data_v2_invisible;

	/**
	 * Data for v3 reCaptcha.
	 *
	 * @var array
	 * @defender_property
	 */
	public $data_v3_recaptcha;

	/**
	 * Data for Cloudflare Turnstile.
	 *
	 * @var array
	 * @defender_property
	 */
	public $data_turnstile;

	/**
	 * Language for Captcha.
	 *
	 * @var string
	 * @defender_property
	 * @rule required
	 */
	public $language = '';

	/**
	 * Message for reCaptcha.
	 *
	 * @var string
	 * @defender_property
	 * @sanitize sanitize_textarea_field
	 */
	public $message = '';

	/**
	 * Locations for Captcha.
	 *
	 * @var array
	 * @defender_property
	 * @rule required
	 */
	public $locations = array();

	/**
	 * Flag to detect WooCommerce.
	 *
	 * @var bool
	 * @defender_property
	 */
	public $detect_woo = false;

	/**
	 * Checked locations for WooCommerce.
	 *
	 * @var array
	 * @defender_property
	 * @rule required
	 */
	public $woo_checked_locations = array();

	/**
	 * Flag to detect BuddyPress.
	 *
	 * @var bool
	 * @defender_property
	 */
	public $detect_buddypress = false;

	/**
	 * Checked locations for BuddyPress.
	 *
	 * @var array
	 * @defender_property
	 * @rule required
	 */
	public $buddypress_checked_locations = array();

	/**
	 * Flag to disable Captcha for known users.
	 *
	 * @var bool
	 * @defender_property
	 */
	public $disable_for_known_users = true;

	/**
	 * Rules for validating the Captcha settings.
	 *
	 * @var array
	 */
	protected $rules = array(
		array( array( 'enabled', 'detect_woo', 'detect_buddypress' ), 'boolean' ),
		array( array( 'active_type' ), 'in', array( 'v2_checkbox', 'v2_invisible', 'v3_recaptcha', self::TURNSTILE ) ),
	);

	/**
	 * Retrieves the default values for the Captcha settings.
	 *
	 * @return array An associative array containing the default values.
	 */
	public function get_default_values(): array {
		return array(
			'message'           => esc_html__( 'reCAPTCHA verification failed. Please try again.', 'wpdef' ),
			'turnstile_message' => esc_html__( 'Turnstile verification failed. Please try again.', 'wpdef' ),
		);
	}

	/**
	 *  Load default values.
	 *
	 * @return void
	 */
	protected function before_load(): void {
		$default_values          = $this->get_default_values();
		$this->provider          = 'recaptcha';
		$this->message           = $default_values['message'];
		$this->language          = 'automatic';
		$this->data_v2_checkbox  = array(
			'key'    => '',
			'secret' => '',
			'size'   => 'normal',
			'style'  => 'light',
		);
		$this->data_v2_invisible = array(
			'key'    => '',
			'secret' => '',
		);
		$this->data_v3_recaptcha = array(
			'key'       => '',
			'secret'    => '',
			'threshold' => '0.5',
		);
		$this->data_turnstile    = array(
			'key'      => '',
			'secret'   => '',
			'size'     => 'normal',
			'style'    => 'auto',
			'message'  => $default_values['turnstile_message'],
			'language' => 'auto',
		);
	}

	/**
	 * Checks if the given Captcha type is valid and has all the necessary data.
	 *
	 * @param  string $active_type  The Captcha type to check.
	 *
	 * @return bool Returns true if the Captcha type is valid and has all the necessary data, false otherwise.
	 */
	private function check_captcha_type( string $active_type ): bool {
		if (
			'v2_checkbox' === $active_type
			&& '' !== $this->data_v2_checkbox['key']
			&& '' !== $this->data_v2_checkbox['secret']
		) {
			return true;
		} elseif (
			'v2_invisible' === $active_type
			&& '' !== $this->data_v2_invisible['key']
			&& '' !== $this->data_v2_invisible['secret']
		) {
			return true;
		} elseif (
			'v3_recaptcha' === $active_type
			&& '' !== $this->data_v3_recaptcha['key'] && '' !== $this->data_v3_recaptcha['secret']
		) {
			return true;
		} elseif (
			self::TURNSTILE === $active_type &&
			'' !== $this->data_turnstile['key'] &&
			'' !== $this->data_turnstile['secret']
		) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Checks if the CAPTCHA is active.
	 *
	 * @return bool Returns true if the CAPTCHA is active, false otherwise.
	 */
	public function is_active(): bool {
		$hook_name = self::TURNSTILE === $this->active_type ? 'wd_turnstile_enable' : 'wd_recaptcha_enable';
		$enable    = apply_filters(
			$hook_name,
			$this->enabled
			&& '' !== $this->active_type
			&& '' !== $this->language
			// For each Captcha type.
			&& $this->check_captcha_type( $this->active_type )
		);

		return is_bool( $enable ) ? $enable : (bool) $enable;
	}

	/**
	 * Is activated any default location?
	 *
	 * @return bool
	 */
	public function enable_default_location(): bool {
		return array() !== $this->locations;
	}

	/**
	 * Level#2 check by any activated location. Only if the plugin is enabled.
	 *
	 * @return bool
	 */
	public function enable_woo_location(): bool {
		return $this->detect_woo && array() !== $this->woo_checked_locations;
	}

	/**
	 * Level#2 check by deactivated locations. Only if the plugin is enabled.
	 *
	 * @return bool
	 */
	public function is_unchecked_woo_locations(): bool {
		return $this->detect_woo && array() === $this->woo_checked_locations;
	}

	/**
	 * Level#1 check. If the plugin is disabled, there is no point further.
	 *
	 * @param  bool $is_woo_activated  Whether WooCommerce is activated or not.
	 *
	 * @return bool
	 */
	public function check_woo_locations( $is_woo_activated ): bool {
		if ( ! $is_woo_activated ) {
			return false;
		}

		return $this->enable_woo_location();
	}

	/**
	 * Level#2 check by any activated location. Only if the plugin is enabled.
	 *
	 * @return bool
	 */
	public function enable_buddypress_location(): bool {
		return $this->detect_buddypress && array() !== $this->buddypress_checked_locations;
	}

	/**
	 * Level#2 check by deactivated locations. Only if the plugin is enabled.
	 *
	 * @return bool
	 */
	public function is_unchecked_buddypress_locations(): bool {
		return $this->detect_buddypress && array() === $this->buddypress_checked_locations;
	}

	/**
	 * Level#1 Checks if the BuddyPress locations are valid and have all the necessary data.
	 *
	 * @param  bool $is_buddypress_activated  Whether BuddyPress is activated or not.
	 *
	 * @return bool Returns true if the BuddyPress locations are valid and have all the necessary data, false otherwise.
	 */
	public function check_buddypress_locations( $is_buddypress_activated ): bool {
		if ( ! $is_buddypress_activated ) {
			return false;
		}

		return $this->enable_buddypress_location();
	}

	/**
	 * Define settings labels.
	 *
	 * @return array
	 */
	public function labels(): array {
		return array(
			'enabled'                 => self::get_module_name(),
			'active_type'             => esc_html__( 'Configure CAPTCHA', 'wpdef' ),
			'v2_checkbox'             => esc_html__( 'V2 Checkbox', 'wpdef' ),
			'v2_invisible'            => esc_html__( 'V2 Invisible', 'wpdef' ),
			'v3_recaptcha'            => esc_html__( 'reCAPTCHA V3', 'wpdef' ),
			'language'                => esc_html__( 'Language', 'wpdef' ),
			'message'                 => esc_html__( 'Error Message', 'wpdef' ),
			'locations'               => esc_html__( 'CAPTCHA Locations', 'wpdef' ),
			'detect_woo'              => esc_html__( 'WooCommerce', 'wpdef' ),
			'detect_buddypress'       => esc_html__( 'BuddyPress', 'wpdef' ),
			'disable_for_known_users' => esc_html__( 'Disable for logged in users', 'wpdef' ),
		);
	}

	/**
	 * Validates the form after submission.
	 *
	 * @return void
	 */
	protected function after_validate(): void {
		$provider_label = self::TURNSTILE === $this->provider ? __( 'Turnstile', 'wpdef' ) : __( 'reCAPTCHA', 'wpdef' );
		// Case with multi errors.
		if ( $this->is_unchecked_woo_locations() && $this->is_unchecked_buddypress_locations() ) {
			// The text of the notation is only in the first key, but we add the number of keys depending on the disabled locations of the plugins.
			/* translators: Provider label. */
			$message = esc_html__(
				'You have enabled %s for more than one plugin. Please select at least one form location for each plugin and click Save Changes again.',
				'wpdef'
			);

			$this->errors['enable_woo']        = sprintf( $message, $provider_label );
			$this->errors['enable_buddypress'] = '';
		} else {
			// Individual cases with plugins.
			if ( $this->is_unchecked_woo_locations() ) {
				/* translators: Provider label. */
				$message = esc_html__(
					'%s for WooCommerce is enabled, but no WooCommerce forms are selected. Please select at least one WooCommerce form location and then click Save Changes again.',
					'wpdef'
				);

				$this->errors['enable_woo'] = sprintf( $message, $provider_label );
			}
			if ( $this->is_unchecked_buddypress_locations() ) {
				/* translators: Provider label. */
				$message = esc_html__(
					'%s for BuddyPress is enabled, but no BuddyPress forms are selected. Please select at least one BuddyPress form location and then click Save Changes again.',
					'wpdef'
				);

				$this->errors['enable_buddypress'] = sprintf( $message, $provider_label );
			}
		}
	}

	/**
	 * Disable for logged in users or enable.
	 *
	 * @return bool
	 */
	public function display_for_known_users(): bool {
		if ( self::TURNSTILE === $this->provider ) {
			return true;
		}
		return ! ( $this->disable_for_known_users && is_user_logged_in() );
	}

	/**
	 * Get module name.
	 *
	 * @return string
	 */
	public static function get_module_name(): string {
		return esc_html__( 'CAPTCHA', 'wpdef' );
	}

	/**
	 * Get active captcha data based on provider and active type.
	 *
	 * @param string|null $provider The provider name.
	 * @param string|null $active_type The active type.
	 *
	 * @return array The captcha data for the specified provider.
	 */
	public function get_active_captcha_data( ?string $provider = null, ?string $active_type = null ): array {
		$provider    = $provider ?? $this->provider;
		$active_type = $active_type ?? $this->active_type;
		if ( self::TURNSTILE === $provider ) {
			return $this->data_turnstile;
		}
		return match ( $active_type ) {
			'v2_checkbox' => $this->data_v2_checkbox,
			'v2_invisible' => $this->data_v2_invisible,
			'v3_recaptcha' => $this->data_v3_recaptcha,
			default => array(),
		};
	}
}