Files
2026-04-28 15:13:50 +02:00

362 lines
12 KiB
PHP

<?php
if (!defined('ABSPATH')) {
exit;
}
/**
* Create turnstile field template.
*
* @param int $button_id
* @param string $callback
* @param string $form_name
* @param string $unique_id
* @param string $class
*/
function cfturnstile_field_show($button_id = '', $callback = '', $form_name = '', $unique_id = '', $class = '') {
// Hook to not show
$hide = apply_filters('cfturnstile_widget_disable', false);
if($hide) {
return;
}
// Check if whitelisted
if(!cfturnstile_whitelisted()) {
// If failsafe enabled and Cloudflare appears down, either hide Turnstile or render reCAPTCHA via helper
if ( get_option('cfturnstile_failover') && cfturnstile_is_cloudflare_down() ) {
$failsafe_type = get_option('cfturnstile_failsafe_type', 'allow');
if ( $failsafe_type === 'recaptcha' ) {
cfturnstile_render_recaptcha_widget($unique_id);
return;
}
// Failsafe set to allow: do not render Turnstile, but post a marker so validation can pass
cfturnstile_render_allow_failsafe_marker();
return;
}
// Show Turnstile
do_action("cfturnstile_enqueue_scripts");
do_action("cfturnstile_before_field", esc_attr($unique_id));
if ( get_option('cfturnstile_widget_label_enable', 0) ) {
$label_text = get_option('cfturnstile_widget_label_text');
$label_text = is_string($label_text) ? trim($label_text) : '';
if ($label_text === '') {
$label_text = __('Let us know you are human:', 'simple-cloudflare-turnstile');
} else {
$label_text = wp_strip_all_tags($label_text);
}
?>
<p class="cfturnstile-widget-label" style="font-size: 14px; margin: 0 0 6px 0; width: 100%;"><small><?php echo esc_html($label_text); ?></small></p>
<?php
}
$key = sanitize_text_field(get_option('cfturnstile_key'));
$theme = sanitize_text_field(get_option('cfturnstile_theme'));
$language = sanitize_text_field(get_option('cfturnstile_language'));
$appearance = sanitize_text_field(get_option('cfturnstile_appearance', 'always'));
$cfturnstile_size = sanitize_text_field(get_option('cfturnstile_size'), 'normal');
$refresh_timeout = sanitize_text_field(get_option('cfturnstile_refresh_timeout', 'auto'));
if(!$language) { $language = 'auto'; }
if(!$refresh_timeout) { $refresh_timeout = 'auto'; }
?>
<div id="cf-turnstile<?php echo esc_attr($unique_id); ?>"
class="cf-turnstile<?php if($class) { echo " " . esc_attr($class); } ?>" <?php if (get_option('cfturnstile_disable_button')) { ?>data-callback="<?php echo esc_attr($callback); ?>"<?php } ?>
data-sitekey="<?php echo esc_attr($key); ?>"
data-theme="<?php echo esc_attr($theme); ?>"
data-language="<?php echo esc_attr($language); ?>"
data-size="<?php echo esc_attr($cfturnstile_size); ?>"
data-retry="auto" data-retry-interval="1000"
data-refresh-expired="auto"
data-refresh-timeout="<?php echo esc_attr($refresh_timeout); ?>"
data-action="<?php echo esc_attr($form_name); ?>"
data-callback="<?php echo esc_attr($callback); ?>"
<?php if(get_option('cfturnstile_failure_message_enable')) { ?>
data-error-callback="cfturnstileErrorCallback"
<?php } ?>
data-appearance="<?php echo esc_attr($appearance); ?>"></div>
<?php
do_action("cfturnstile_after_field", esc_attr($unique_id), $button_id);
}
}
/**
* Add Styles Below Turnstile if Disable Submit Enabled
*
* @return bool
*/
add_action('cfturnstile_after_field', 'cfturnstile_disable_button_styles', 10, 2);
function cfturnstile_disable_button_styles($unique_id, $button_id) {
if ($button_id && get_option('cfturnstile_disable_button')) {
?>
<style><?php echo esc_html($button_id); ?> { pointer-events: none; opacity: 0.5; }</style>
<?php
}
}
/**
* Add a line break if Turnstile is always showing
*
* @return bool
*/
add_action('cfturnstile_after_field', 'cfturnstile_always_br', 15, 1);
function cfturnstile_always_br($unique_id) {
if(!get_option('cfturnstile_appearance') || get_option('cfturnstile_appearance') == 'always') {
?>
<br class="cf-turnstile-br cf-turnstile-br<?php echo esc_attr($unique_id); ?>">
<?php
} else {
?>
<style>#cf-turnstile<?php echo esc_html($unique_id); ?> iframe { margin-bottom: 15px; }</style>
<?php
}
}
/**
* Extra Styles if WP Admin
*
* @return bool
*/
add_action('cfturnstile_after_field', 'cfturnstile_admin_styles', 20, 1);
function cfturnstile_admin_styles($unique_id) {
if(defined('DOING_AJAX') || is_admin()) {
return;
}
$is_checkout = (function_exists('is_checkout') && is_checkout()) ? true : false;
if ((!is_page() && !is_single() && !$is_checkout) || strpos($_SERVER['PHP_SELF'], 'wp-login.php') !== false) {
?>
<style>#cf-turnstile<?php echo esc_html($unique_id); ?> { margin-left: -15px; }</style>
<?php
}
}
/**
* Show custom failed message after Turnstile if failed
*
* @return bool
*/
add_action('cfturnstile_after_field', 'cfturnstile_failed_text', 5, 1);
function cfturnstile_failed_text($unique_id) {
if(function_exists('cfturnstile_is_block_based_checkout') && cfturnstile_is_block_based_checkout()) {
return;
}
if(get_option('cfturnstile_failure_message_enable')) {
$failed_text = get_option('cfturnstile_failure_message');
if(!$failed_text) { $failed_text = esc_html__('Failed to verify you are human. Please contact us if you are having issues.', 'simple-cloudflare-turnstile'); }
$failed_text = str_replace("'", "\'", $failed_text);
$failed_text = str_replace('"', '\"', $failed_text);
?>
<div class="cf-turnstile-failed-text cf-turnstile-failed-text<?php echo esc_attr($unique_id); ?>"></div>
<script>
function cfturnstileErrorCallback() {
var cfTurnstileFailedText = document.querySelector('.cf-turnstile-failed-text<?php echo esc_html($unique_id); ?>');
cfTurnstileFailedText.innerHTML = '<p><i><?php echo wp_kses_post($failed_text); ?></i></p>';
}
function cfturnstileCallback() {
var cfTurnstileFailedText = document.querySelector('.cf-turnstile-failed-text<?php echo esc_html($unique_id); ?>');
cfTurnstileFailedText.innerHTML = '';
}
</script>
<?php
}
}
/**
* Render Turnstile (Explicitly)
*/
add_action("cfturnstile_after_field", "cfturnstile_force_render", 10, 1);
function cfturnstile_force_render($unique_id = '') {
if(function_exists('cfturnstile_is_block_based_checkout') && cfturnstile_is_block_based_checkout()) {
return;
}
$unique_id = sanitize_text_field($unique_id);
$key = sanitize_text_field(get_option('cfturnstile_key'));
if($unique_id) {
?>
<script>document.addEventListener("DOMContentLoaded", function() { setTimeout(function(){ var e=document.getElementById("cf-turnstile<?php echo esc_html($unique_id); ?>"); if(e&&!e.innerHTML.trim()){turnstile.render(e, {sitekey:"<?php echo esc_html($key); ?>"});} }, 200); });</script>
<?php
}
}
/**
* Checks Turnstile Captcha POST is Valid
*
* @param string $postdata
* @return bool
*/
function cfturnstile_check($postdata = "") {
$results = array();
// Check if whitelisted
if(cfturnstile_whitelisted()) {
$results['success'] = true;
return $results;
}
// Hook to allow custom skip
$skip = apply_filters('cfturnstile_widget_disable', false);
if($skip) {
$results['success'] = true;
return $results;
}
// Check if POST data is empty
if (empty($postdata) && isset($_POST['cf-turnstile-response'])) {
$postdata = sanitize_text_field($_POST['cf-turnstile-response']);
}
// If failsafe is present, handle it early
if ( get_option('cfturnstile_failover') && isset($_POST['cfturnstile_failsafe']) && cfturnstile_is_cloudflare_down() ) {
$failsafe_flag = sanitize_text_field($_POST['cfturnstile_failsafe']);
$failsafe_type = get_option('cfturnstile_failsafe_type', 'allow');
if ( $failsafe_flag === 'recaptcha' && $failsafe_type === 'recaptcha' ) {
return cfturnstile_verify_recaptcha();
}
if ( $failsafe_flag === 'allow' && $failsafe_type === 'allow' ) {
return array('success' => true);
}
}
// Get Turnstile Keys from Settings
$key = sanitize_text_field(get_option('cfturnstile_key'));
$secret = sanitize_text_field(get_option('cfturnstile_secret'));
if ($key && $secret) {
$headers = array(
'body' => [
'secret' => $secret,
'response' => $postdata,
'remoteip' => cfturnstile_get_ip(),
]
);
$verify = wp_remote_post('https://challenges.cloudflare.com/turnstile/v0/siteverify', $headers);
// Failover if Cloudflare is down (centralized handler)
$handled = cfturnstile_handle_failover_backend($verify);
if ( $handled !== null ) {
return $handled;
}
$verify = wp_remote_retrieve_body($verify);
$response = json_decode($verify);
if ( ! is_object( $response ) ) {
$results['success'] = false;
return $results;
}
if($response->success) {
$results['success'] = $response->success;
} else {
$results['success'] = false;
}
foreach ( $response as $key => $val ) {
if ( 'error-codes' === $key ) {
foreach ( $val as $key => $error_val ) {
$results['error_code'] = $error_val;
if ( 'invalid-input-secret' === $error_val ) {
// Rate-limit: only process once per 5 minutes to avoid repeated DB writes on high-traffic sites.
if ( false === get_transient( 'cfturnstile_invalid_secret_throttle' ) ) {
set_transient( 'cfturnstile_invalid_secret_throttle', 1, 5 * MINUTE_IN_SECONDS );
$already_flagged = ( 'no' === get_option( 'cfturnstile_soft_tested' ) );
update_option( 'cfturnstile_invalid_secret_notice', '1' );
update_option( 'cfturnstile_soft_tested', 'no' );
if ( ! $already_flagged ) {
$admin_email = get_option( 'admin_email' );
$site_name = get_bloginfo( 'name' );
$settings_url = admin_url( 'options-general.php?page=cfturnstile' );
$subject = sprintf(
/* translators: %s: Site name. */
__( '[%s] Cloudflare Turnstile: Invalid Secret Key Detected', 'simple-cloudflare-turnstile' ),
$site_name
);
$message = sprintf(
/* translators: 1: Site name, 2: Settings page URL. */
__( "Cloudflare has reported that the Turnstile secret key on %1\$s is invalid (error: invalid-input-secret).\n\nTurnstile is still active on your forms, but verifications may be failing until the key is corrected.\n\nPlease check your API keys on the settings page:\n%2\$s", 'simple-cloudflare-turnstile' ),
$site_name,
$settings_url
);
wp_mail( $admin_email, $subject, $message );
}
}
}
}
}
}
do_action('cfturnstile_after_check', $response, $results);
return $results;
} else {
return array( 'success' => false );
}
}
/*
* Add Turnstile check to a "cfturnstile_log" option
*/
add_action('cfturnstile_after_check', 'cfturnstile_log', 10, 2);
function cfturnstile_log($response, $results) {
if(get_option('cfturnstile_log_enable')) {
// Get log
$cfturnstile_log = get_option('cfturnstile_log');
if(!$cfturnstile_log) {
$cfturnstile_log = array();
}
// If $results['error_code'] is not set, set it to empty
if(!isset($results['error_code'])) {
$results['error_code'] = '';
}
// Get Values
$error_code = $results['error_code'];
// Success Yes or No
if($response->success) {
$success = true;
} else {
$success = false;
}
// Add to log
$cfturnstile_log[] = array(
'date' => date('Y-m-d H:i:s'),
'success' => $success,
'error' => $error_code,
'ip' => cfturnstile_get_ip(),
'page' => isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '',
);
// Max 50
if(count($cfturnstile_log) > 50) {
array_shift($cfturnstile_log);
}
// Update log
update_option('cfturnstile_log', $cfturnstile_log);
}
}
/**
* Check if form should show Turnstile
*/
function cfturnstile_form_disable($id, $option) {
if(!empty(get_option($option)) && get_option($option)) {
$disabled = preg_replace('/\s+/', '', get_option($option));
$disabled = explode (",",$disabled);
if(in_array($id, $disabled)) return true;
}
return false;
}
/**
* Create shortcode to display Turnstile widget
*/
add_shortcode('simple-turnstile', 'cfturnstile_shortcode');
add_action('cfturnstile_display_widget', 'cfturnstile_shortcode', 10, 0);
function cfturnstile_shortcode() {
ob_start();
echo cfturnstile_field_show('', '');
$thecontent = ob_get_contents();
ob_end_clean();
wp_reset_postdata();
$thecontent = trim(preg_replace('/\s+/', ' ', $thecontent));
return $thecontent;
}