<?php

/**
 * This file is part of an ADDON for use with LEPTON Core.
 * This ADDON is released under the GNU GPL.
 * Additional license terms can be seen in the info.php of this module.
 *
 *
 *	@module			captcha
 *	@version		see info.php of this module
 *	@authors		LEPTON Project, W. Studer
 *	@copyright		2020-2022 LEPTON Project
 *	@link			https://lepton-cms.org
 *	@license		http://www.gnu.org/licenses/gpl.html
 *	@license_terms	please see info.php of this module
 *	@platform		see info.php of this module
 *
 *
 */


// include the trait(s)
require_once( LEPTON_PATH . "/modules/captcha_control/classes/plugin_trait.php" );

// create plugin class
class text
{
	private $langCodes = array();

	// use trait(s)
	use plugin_trait;

	/* ===============================================================================
	 * define defaults for build_captcha
	 *
	 * scope:	frontend,backend
	 * access:	private
	 * param:	none
	 * output:	none
	 */
	private function define_defaults()
	{
		// set plugin specific defaults
		$defaults = array(
			"TEXT_ATTR"		=> array(
									"class"		=> "captcha_text"
								),
			"IMAGE_ATTR"	=> array(
									"class"		=> "captcha_image"
								),
			"INPUT_ATTR"	=> array(
									"type"		=> "text",
									"class"		=> "captcha_input",
									"maxlength"	=> "50",
									"required"	=> "required"
								)
		);

		// merge plugin specific defaults with generic defaults
		$this->defaults = $this->merge_arrays( $defaults, $this->defaults );

		// define the captcha input field
		$this->captcha_field = array( "KEY" => "captcha"
									,"REQUEST" => array ('type' => 'string_clean', 'default' => "" ));

		// define list of supported actions
		$this->supported_actions = array( "all", "image", "input", "text", "js", "data" );

		// mark plugin as deprecated
		$this->is_deprecated = false;

		// define if plugin is useable in current configuration
		$this->is_useable = true;
	}

	/* ===============================================================================
	 * get fields for captcha maintenance in admin module captcha_control
	 *
	 * scope:	backend
	 * access:	public
	 * param:	none
	 * output:	array containing list of fields shown in backend gui
	 */
	public function get_control_fieldset()
	{
		// get db fields
		$db_fieldset = $this->get_db_fields();

		// return array of field definition(s)
		$fieldset = array(
			"language"		=> $this->language,
			"NBR_QUESTIONS"	=> array(
				 "VALUE"	=> $db_fieldset[ "nbr_questions" ][ "default" ]
			),
			"CASE_SENSITIVE"=> array(
				 "VALUE"	=> $db_fieldset[ "case_sensitive" ][ "default" ]
			),
			"TABLE_QA"		=> array(),
			"LANGCODES"		=> $this->langCodes
		);

		if ( count( $this->langCodes ) == 0 )
			{ $this->langCodes[] = LANGUAGE; }

		// prepare rows with data columns
		foreach( $this->langCodes as $lang )
		{
			$rows = array();
			for ( $i = 1; $i <= $db_fieldset[ "nbr_questions" ][ "default" ]; $i++ )
			{
				$rows[ $i ]	= array(
					"I" 		=> array(
						"VALUE"		=> $i
					),
					"Q" 		=> array(
						"VALUE"		=> ( array_key_exists(  $lang . "_question_" . $i, $db_fieldset )
											? $db_fieldset[ $lang . "_question_" . $i ][ "default" ]
											: "" )
								
					),
					"A"		 => array(
						"VALUE"		=> ( array_key_exists(  $lang . "_answer_" . $i, $db_fieldset )
											? $db_fieldset[ $lang . "_answer_" . $i ][ "default" ]
											: "" )
					)
				);
			}	// end loop number of questions
			
			$fieldset[ "TABLE_QA" ][ $lang ] = $rows;
		}

		// add additional languages
		$database = LEPTON_database::getInstance();
		$add_languages = array();
		$database->execute_query(
			 "SELECT `directory`,`name`"
				. " FROM `" . TABLE_PREFIX . "addons`"
				. " WHERE `type` = 'language'"
				. "   AND `directory` NOT IN ('" . implode ( "','" , $this->langCodes )  . "')"
				. " ORDER BY `directory`"
			,true
			,$add_languages
			,true
		);
		$fieldset[ "ADD_LANGUAGE" ] = $add_languages;

		return $fieldset;
	}

	/* ===============================================================================
	 * get captcha maintenance fields saved in db
	 *
	 * scope:	backend
	 * access:	public
	 * param:	none
	 * output:	array containing db related fields and their input validations
	 */
	public function get_db_fields()
	{
		// get number of questions
		$nbr_questions = ( array_key_exists( "nbr_questions", $this->settings[ "PLUGIN" ] )
								? $this->settings[ "PLUGIN" ][ "nbr_questions" ]
								: 3 );
		$case_sensitive = ( array_key_exists( "case_sensitive", $this->settings[ "PLUGIN" ] )
								? $this->settings[ "PLUGIN" ][ "case_sensitive" ]
								: "0" );

		// set single fields
		$fieldset = array(
			"nbr_questions"		=> array( "type" => "integer+", "default" => $nbr_questions, "range" => array( "min" => 1, "max" => 10 ) ),
			"case_sensitive"		=> array( "type" => "string_clean", "default" => $case_sensitive )
		);

		// get language dependend Q & A
		$this->langCodes = array();
		if ( count( $this->settings[ "PLUGIN" ] ) > 0 )
		{
			foreach( $this->settings[ "PLUGIN" ] as $key => $value )
			{
				if ( strpos( $key, "_" ) > 2 )
					{ continue; }

				$lang = substr( $key, 0, 2);
				$this->langCodes[] = $lang;

				$fieldset[ $key ]	= array( "type" => "string_clean", "default" => $value );
			}	// end loop
		}
		if ( count( $this->langCodes ) == 0 )
		{
			$this->langCodes[] = LANGUAGE;
		}

		// complete with missing elements within Q&A
		foreach( $this->langCodes as $lang )
		{
			for ( $i = 1; $i <= $nbr_questions; $i++ )
			{
				$key = $lang . "_question_" . $i;
				if ( false === array_key_exists( $key, $fieldset ))
				{
					$fieldset[ $key ]	= array( "type" => "string_clean", "default" => "" );
				}
				$key = $lang . "_answer_" . $i;
				if ( false === array_key_exists( $key, $fieldset ))
				{
					$fieldset[ $key ]	= array( "type" => "string_clean", "default" => "" );
				}
			}
		}

		return $fieldset;
	}

	/* ===============================================================================
	 * calculate the captcha string
	 *
	 * scope:	frontend
	 * access:	public
	 * param:	array with attributes
	 * output:	the captcha
	 */
	public function calculate_captcha( $params )
	{
		$section_id = $_SESSION[ 'captcha_id' ];

		// calculate/build the captcha
		$nbr_questions = ( array_key_exists( "nbr_questions", $this->settings[ "PLUGIN" ] )
								? $this->settings[ "PLUGIN" ][ "nbr_questions" ]
								: "3" );

		$_SESSION['captcha' . $section_id ] = '';
		mt_srand((double)microtime()*1000000);

		// loop till at least 1 Q & A found with values
		$captcha = "";
		do
		{
			$i = mt_rand(1, $nbr_questions );

			// evaluate language to be used
			$lang = "";
			if ( array_key_exists( LANGUAGE . "_question_" . $i, $this->settings[ "PLUGIN" ] ))
				{ $lang = LANGUAGE; }
			elseif ( array_key_exists( "EN" . "_question_" . $i, $this->settings[ "PLUGIN" ] ))
				{ $lang = "EN"; }
			elseif ( array_key_exists( "XX" . "_question_" . $i, $this->settings[ "PLUGIN" ] ))
				{ $lang = "XX"; } // old value from pre LEPTON5 version. No longer able to create!
			else
				{ $lang = ""; }

			// get question
			$Q = ( array_key_exists( $lang . "_question_" . $i, $this->settings[ "PLUGIN" ] )
					? $this->settings[ "PLUGIN" ][ $lang . "_question_" . $i ]
					: "" );
			// get answer
			$A = ( array_key_exists( $lang . "_answer_" . $i, $this->settings[ "PLUGIN" ] )
					? $this->settings[ "PLUGIN" ][ $lang . "_answer_" . $i ]
					: "" );

			// found a + & A with values
			if (( false === empty( $Q )) && ( false === empty( $A )))
			{
				$_SESSION['captcha' . $section_id] = $A;
				$captcha = $Q;
				break;	// exit dow while loop
			}
			
			$nbr_questions--;
		} while ( $nbr_questions > 0 );

		// nothing found, use default
		if ( true === empty( $captcha ))
		{
			$_SESSION['captcha' . $section_id] = $this->language['DEFAULT_ANSWER'];
			$captcha = $this->language['DEFAULT_QUESTION'];
		}

		// prepare for reload and speech
		$_SESSION['captcharequest' . $section_id] = array(
			"RESULT"  => $_SESSION['captcha' . $section_id],
			"CAPTCHA" => $captcha,
			"SPEECH"  => $captcha
		);

		return $captcha;
	}

	/* ===============================================================================
	 * do plugin specific actions before plugin related settings are saved
	 *
	 * scope:	backend
	 * access:	public
	 * param:	array containing plugin related settings
	 * output:	array containing plugin related settings after validation (if any)
	 */
	public function settings_save( $settings )
	{
		// get number of defined questions
		$nbr_questions = ( array_key_exists( "nbr_questions", $settings )
								? $settings[ "nbr_questions" ]
								: 3 );
		$case_sensitive = ( array_key_exists( "case_sensitive", $settings )
								? $settings[ "case_sensitive" ]
								: "0" );

		// ignore all empty as well as all over defined number of questions
		$cleared = array( 
			 "nbr_questions" => $nbr_questions
			,"case_sensitive" => $case_sensitive
		);
		foreach( $this->langCodes as $lang )
		{
			$rows = array();
			$c = 0;
			for ( $i = 1; $i <= $nbr_questions; $i++ )
			{
				$question	= ( array_key_exists( $lang . "_question_" . $i, $settings )
									? $settings[ $lang . "_question_" . $i ]
									: "" );
				$answer		= (  array_key_exists( $lang . "_answer_" . $i, $settings )
									? $settings[ $lang . "_answer_" . $i ]
									: "" );

				if (( false === empty( trim( $question ))) && ( false === empty( trim( $answer ))))
				{
					$c++;
					$cleared[ $lang . "_question_" . $c ] = $question;
					$cleared[ $lang . "_answer_" . $c ]   = $answer;
				}
			}
		}

		// check for additional language
		$validate_fields[ "add_language" ] = array( "type" => "string_clean", "default" => "" );
		$oREQUEST = LEPTON_request::getInstance();
		$valid_fields = $oREQUEST->testPostValues( $validate_fields );

		if ( false === empty( $valid_fields[ "add_language" ] ))
		{
			$lang = $valid_fields[ "add_language" ];
			$c = 1;
			$cleared[ $lang . "_question_" . $c ] = "?";
			$cleared[ $lang . "_answer_" . $c ]   = "!";
		}

		return $cleared;
	}

	/* ===============================================================================
	 * test a captcha
	 *
	 * scope:	frontend
	 * access:	public
	 * param:	$value, $section_id
	 * output:	the captcha
	 */
	public function test_captcha( $value, $section_id )
	{
		// get value if not passed as parameter
		if ( true === empty( $value ))
		{
			$oREQUEST = LEPTON_request::getInstance();
			$input_fields = array (
				$this->captcha_field["KEY"]	=> $this->captcha_field["REQUEST"]
			);
			$valid_fields = $oREQUEST->testPostValues($input_fields);
			$value = $valid_fields[ $this->captcha_field["KEY"] ];
		}

		// if compare is not case sensitive
		if ( false === array_key_exists( "case_sensitive", $this->settings[ "PLUGIN" ] ))
			{ $this->settings[ "PLUGIN" ][ "case_sensitive" ] = 0; }
		if ( 0 == $this->settings[ "PLUGIN" ][ "case_sensitive" ] )
		{
			$value = trim( strtolower( $value ));
			$_SESSION[ "captcha" . $section_id ] = trim( strtolower( $_SESSION[ "captcha" . $section_id ] ));
		}

		// start verify
		if ( isset( $_SESSION[ "captcha" . $section_id ] ))
		{
			if ( $_SESSION[ "captcha" . $section_id ] == $value )
					{ return true; }	// correct value
			else	{ return false; }	// wrong value
		}
		else		{ return true; }	// no captcha has been set
	}


}

?>
