using_single_recaptcha() ) { return $referrer_url; } if ( $this->has_recaptcha_parameter( $referrer_url ) ) { $referrer_url = add_query_arg( self::TYPE_PARAM, false, $referrer_url ); } return $referrer_url; } public function using_recaptcha() { return ppw_core_get_setting_type_bool_by_option_name( PPW_Constants::USING_RECAPTCHA, PPW_Constants::EXTERNAL_OPTIONS ); } /** * Add blocked message if user turn on option. * * @param string $redirect_url Redirect URL. * @param array $params Parameters. * * @return string */ public function maybe_add_blocked_message( $redirect_url, $params ) { if ( ! $this->using_single_recaptcha() ) { return $redirect_url; } if ( $params['is_valid'] ) { return $redirect_url; } if ( ! $this->show_message ) { // Remove blocked parameter if URL has it. if ( $this->has_recaptcha_parameter( $redirect_url ) ) { $redirect_url = add_query_arg( self::TYPE_PARAM, false, $redirect_url ); } return $redirect_url; } $redirect_url = add_query_arg( self::TYPE_PARAM, self::TYPE_VALUE, $redirect_url ); return $redirect_url; } /** * Has recaptcha parameter on URL. * * @param string $url $url URL. * @param string $query_value Query value. * * @return bool */ private function has_recaptcha_parameter( $url = '', $query_value = self::TYPE_VALUE ) { if ( empty( $url ) ) { $query_params = ppw_core_get_query_param(); } else { $query_params = ppw_core_get_param_in_url( $url ); } if ( ! isset( $query_params[ self::TYPE_PARAM ] ) ) { return false; } return $query_value === $query_params[ self::TYPE_PARAM ]; } /** * Customize error message. * * @param array $params Parameters. * * @return array */ public function maybe_customize_error_message( $params ) { if ( ! $this->using_single_recaptcha() ) { return $params; } if ( $this->has_recaptcha_parameter() ) { $message = $this->get_error_message(); $params['error_msg'] = apply_filters( 'ppw_recaptcha_error_message', $message, $params ); } return $params; } /** * Validate recaptcha. * * @return bool */ public function is_valid_recaptcha() { $_post = wp_unslash( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- We no need to handle nonce verification here because already handle on parent function. if ( ! isset( $_post['g-recaptcha-response'] ) ) { $this->show_message = true; return false; } $result = $this->verify_recaptcha( $_post['g-recaptcha-response'] ); if ( ! $result['success'] ) { $this->show_message = true; return false; } return true; } /** * Get limit score. * * @return double */ public function get_limit_score() { $score = ppw_core_get_settings_by_option_name( PPW_Constants::RECAPTCHA_SCORE, PPW_Constants::EXTERNAL_OPTIONS ); if ( is_null( $score ) ) { return (double) 0.5; } return (double) $score; } /** * Get setting api key of recaptcha with current type. * * @param string $type Recaptcha type. * @param string $default Default value. * * @return string */ public function get_setting_api_key( $type = '', $default = '' ) { if ( empty( $type ) ) { $type = $this->get_recaptcha_type(); } switch ( $type ) { case self::RECAPTCHA_V3_TYPE: return $this->get_recaptcha_v3_api_key(); case self::RECAPTCHA_V2_CHECKBOX_TYPE: return $this->get_recaptcha_v2_api_key(); default: return $default; } } /** * Get setting api secret of recaptcha with current type. * * @param string $type Recaptcha type. * @param string $default Default value. * * @return string */ public function get_setting_api_secret( $type = '', $default = '' ) { if ( empty( $type ) ) { $type = $this->get_recaptcha_type(); } switch ( $type ) { case self::RECAPTCHA_V3_TYPE: return $this->get_recaptcha_v3_api_secret(); case self::RECAPTCHA_V2_CHECKBOX_TYPE: return $this->get_recaptcha_v2_api_secret(); default: return $default; } } /** * Get recaptcha v3 API key. * * @return string */ public function get_recaptcha_v3_api_key() { return ppw_core_get_setting_type_string_by_option_name( PPW_Constants::RECAPTCHA_API_KEY, PPW_Constants::EXTERNAL_OPTIONS ); } /** * Get recaptcha v3 API secret. * * @return string */ public function get_recaptcha_v3_api_secret() { return ppw_core_get_setting_type_string_by_option_name( PPW_Constants::RECAPTCHA_API_SECRET, PPW_Constants::EXTERNAL_OPTIONS ); } /** * Get recaptcha v2 API key. * * @return string */ public function get_recaptcha_v2_api_key() { return ppw_core_get_setting_type_string_by_option_name( PPW_Constants::RECAPTCHA_V2_CHECKBOX_API_KEY, PPW_Constants::EXTERNAL_OPTIONS ); } /** * Get recaptcha v2 API secret. * * @return string */ public function get_recaptcha_v2_api_secret() { return ppw_core_get_setting_type_string_by_option_name( PPW_Constants::RECAPTCHA_V2_CHECKBOX_API_SECRET, PPW_Constants::EXTERNAL_OPTIONS ); } /** * Get recaptcha type. * * @return string */ public function get_recaptcha_type() { $recaptcha_type = ppw_core_get_setting_type_string_by_option_name( PPW_Constants::RECAPTCHA_TYPE, PPW_Constants::EXTERNAL_OPTIONS ); return $recaptcha_type ? $recaptcha_type : self::RECAPTCHA_V3_TYPE; } /** * Get password types selected. * * @return string[] */ public function get_password_types() { $password_types = ppw_core_get_setting_type_array_by_option_name( PPW_Constants::RECAPTCHA_PASSWORD_TYPES, PPW_Constants::EXTERNAL_OPTIONS ); return $password_types ? $password_types : array( 'single' ); } /** * Using single recaptcha * * @return bool */ public function using_single_recaptcha() { return $this->using_recaptcha() && in_array( self::SINGLE_PASSWORD, $this->get_password_types() ); } /** * Using sitewide recaptcha * * @return bool */ public function using_sitewide_recaptcha() { return $this->using_recaptcha() && in_array( self::SITEWIDE_PASSWORD, $this->get_password_types() ); } /** * Using sitewide recaptcha * * @return bool */ public function using_pcp_recaptcha() { return $this->using_recaptcha() && in_array( self::PCP_PASSWORD, $this->get_password_types() ); } /** * Load recaptcha v2 javascript. */ public function load_recaptcha_v2_js() { ob_start(); ?> get_recaptcha_v3_api_key(); ob_start(); ?> false, 'message' => '', ); if ( ! $recaptcha_response ) { return $default; } $secret = $this->get_setting_api_secret(); if ( ! $secret ) { return $default; } $response = wp_remote_post( 'https://www.google.com/recaptcha/api/siteverify', array( 'method' => 'POST', 'timeout' => 45, 'redirection' => 5, 'httpversion' => '1.0', 'blocking' => true, 'headers' => array(), 'body' => array( 'secret' => $secret, 'response' => $recaptcha_response, ), 'cookies' => array(), ) ); if ( is_wp_error( $response ) ) { return $default; } $body = wp_remote_retrieve_body( $response ); $body = json_decode( $body ); // Whether this request was a valid reCAPTCHA token for your site. $success = isset( $body->success ) && $body->success; $external = true; if ( $this->get_recaptcha_type() === self::RECAPTCHA_V3_TYPE ) { $limit_score = $this->get_limit_score(); // The score for this request (0.0 - 1.0) 1.0 is very likely a good interaction, 0.0 is very likely a bot. $score = isset( $body->score ) ? (double) $body->score : 0; $external = $score > $limit_score; } $default['success'] = $success && $external; return $default; } /** * Load PPWPEA api key. * * @param string $key Recaptcha API key. * * @return string */ public function get_ppwpea_recaptcha_v2_api_key( $key ) { return $this->get_recaptcha_v2_api_key(); } /** * Load PPWPEA api secret. * * @param string $secret Recaptcha API secret. * * @return string */ public function get_ppwpea_recaptcha_v2_api_secret( $secret ) { return $this->get_recaptcha_v2_api_secret(); } public function maybe_load_sitewide_recaptcha_js() { if ( ! $this->using_sitewide_recaptcha() ) { return; } $this->add_recaptcha_to_head(); } /** * Add recaptcha to head. */ public function add_recaptcha_to_head() { $recaptcha_type = $this->get_recaptcha_type(); switch ( $recaptcha_type ) { case self::RECAPTCHA_V3_TYPE: $this->load_recaptcha_v3_js(); break; case self::RECAPTCHA_V2_CHECKBOX_TYPE: case self::RECAPTCHA_V2_INVISIBLE_TYPE: $this->load_recaptcha_v2_js(); break; } } /** * Add recaptcha input below sitewide form. */ public function maybe_add_recaptcha_input_below_sitewide_form() { $recaptcha_input = $this->get_recaptcha_input(); if ( ! empty( $recaptcha_input ) ) { echo $recaptcha_input; } } /** * Get recaptcha input. * * @return string */ public function get_recaptcha_input() { switch ( $this->get_recaptcha_type() ) { case PPW_Recaptcha::RECAPTCHA_V2_CHECKBOX_TYPE: $site_key = $this->get_recaptcha_v2_api_key(); return '
'; default: return ''; } } /** * Customize sitewide css. */ public function customize_sitewide_css() { if ( ! $this->using_sitewide_recaptcha() ) { return; } ?> .g-recaptcha { transform:scale(0.9); transform-origin:0 0; } using_sitewide_recaptcha() ) { return $validated; } if ( ! $this->is_valid_recaptcha() ) { add_filter( 'ppw_sitewide_error_message', array( $this, 'get_sitewide_error_message' ) ); return false; } return $validated; } /** * Get sitewide error message. * * @return string */ public function get_sitewide_error_message() { return __( 'Google reCAPTCHA verification failed, please try again later.', 'password-protect-page' ); } /** * Load JS in footer. */ public function load_js_in_footer() { if ( ! $this->using_single_recaptcha() ) { return; } $allowed = is_singular(); $allowed = apply_filters( 'ppw_recaptcha_allowed_to_load_script', $allowed ); if ( ! $allowed ) { return; } $post_id = get_the_ID(); if ( ! $post_id || ! post_password_required( $post_id ) ) { return; } $this->add_recaptcha_to_head(); } }