<?php
/**
 *
 * Note: The class names have been prefixed with pmpro_ to avoid conflicts with other plugins.
 *
 * This is a PHP library that handles calling reCAPTCHA.
 *    - Documentation and latest version
 *          https://developers.google.com/recaptcha/docs/php
 *    - Get a reCAPTCHA API Key
 *          https://www.google.com/recaptcha/admin/create
 *    - Discussion group
 *          http://groups.google.com/group/recaptcha
 *
 * @copyright Copyright (c) 2014, Google Inc.
 * @link      http://www.google.com/recaptcha
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * A ReCaptchaResponse is returned from checkAnswer().
 */
class pmpro_ReCaptchaResponse
{
    public $success;
    public $errorCodes;
}

class pmpro_ReCaptcha
{
    private static $_signupUrl = "https://www.google.com/recaptcha/admin";
    private static $_siteVerifyUrl =
        "https://www.google.com/recaptcha/api/siteverify?";
    private $_secret;
    private static $_version = "php_1.0";

    /**
     * Constructor.
     *
     * @param string $secret shared secret between site and ReCAPTCHA server.
     */
    function __construct($secret)
    {
        if ($secret == null || $secret == "") {
            die("To use reCAPTCHA you must get an API key from <a href='"
                . self::$_signupUrl . "'>" . self::$_signupUrl . "</a>");
        }
        $this->_secret=$secret;
    }

    /**
     * Submits an HTTP POST to a reCAPTCHA server.
     *
     * @param string $path url path to recaptcha server.
     * @param array  $data array of parameters to be sent.
     *
     * @return string response
     */
    private function _submitHTTPPost($path, $data)
    {
        $response = wp_remote_post($path, [
            'method' => 'POST',
            'body'   => $data,
        ])['body'];

        return $response;
    }

    /**
     * Calls the reCAPTCHA siteverify API to verify whether the user passes
     * CAPTCHA test.
     *
     * @param string $remoteIp   IP address of end user.
     * @param string $response   response string from recaptcha verification.
     *
     * @return object recaptchaResponse
     */
    public function verifyResponse($remoteIp, $response)
    {

		// Default to it not being okay.
		$recaptchaResponse = new pmpro_ReCaptchaResponse();
		$recaptchaResponse->success = false;

        // Discard empty solution submissions
        if ($response == null || strlen($response) == 0) {
            $recaptchaResponse->success = false; // Redundant but okay.
            $recaptchaResponse->errorCodes = 'missing-input';
            return $recaptchaResponse;
        }

        $getResponse = $this->_submitHttpPost(
            self::$_siteVerifyUrl,
            array (
                'secret' => $this->_secret,
                'remoteip' => $remoteIp,
                'v' => self::$_version,
                'response' => $response
            )
        );
        $answers = json_decode($getResponse, true);

        // Check if reCAPTCHA validation was successful, then handle V2 or V3.
        if ( ! empty( $answers['success'] ) ) {
			
			// Check for V3 score if present.
			if ( isset( $answers['score'] ) ) {

				/**
				 * Set the minimum score to mark the submission as successful.
				 * 
				 * @since 3.7
				 * 
				 * @param float $min_score Defaults to 0.5 and supports 0.0 - 1.0 values. Any values outside this will default to 0.5.
				 */
				$min_score = (float) apply_filters( 'pmpro_recaptcha_v3_min_score', 0.5 );
				if ( $min_score < 0.0 || $min_score > 1.0 ) {
					$min_score = 0.5;
				}

				if ( (float) $answers['score'] >= $min_score ) {
					$recaptchaResponse->success = true;
				} else {
					$recaptchaResponse->success = false;
					$recaptchaResponse->errorCodes = 'Low reCAPTCHA score';
				}

			} else {
				$recaptchaResponse->success = true; // V2 has no score.
			}
        } else {
            $recaptchaResponse->success = false;
            if (!empty($answers['error-codes'])) {
                switch ($answers['error-codes'][0]) {
                    case 'timeout-or-duplicate':
                        $recaptchaResponse->errorCodes = 'Duplicate or expired reCAPTCHA used';
                        break;
                    case 'missing-input-response':
                    case 'invalid-input-response':
                        $recaptchaResponse->errorCodes = 'Bad reCAPTCHA result';
                        break;
                    case 'missing-input-secret':
                    case 'invalid-input-secret':
                    case 'bad-request':
                        $recaptchaResponse->errorCodes = 'Internal error';
                        break;
                    default:
                        $recaptchaResponse->errorCodes = 'Unknown error';
                        break;
                }
            }
        }

        return $recaptchaResponse;
    }
}
