<?php

/**
 *
 *	@module			quickform
 *	@version		see info.php of this module
 *	@authors		LEPTON project, Ruud Eisinga
 *	@copyright		2012-2021  LEPTON project (initial by Ruud Eisinga)
 *	@license		GNU General Public License
 *	@license terms	see info.php of this module
 *	@platform		see info.php of this module
 *
 *
 */


// include class.secure.php to protect this file and the whole CMS!
if (defined("LEPTON_PATH")) {
	include(LEPTON_PATH."/framework/class.secure.php");
} else {
	$root = "../";
	$level = 1;
	while (($level < 10) && (!file_exists($root."/framework/class.secure.php"))) {
		$root .= "../";
		$level += 1;
	}
	if (file_exists($root."/framework/class.secure.php")) {
		include($root."/framework/class.secure.php");
	} else {
		trigger_error(sprintf("[ <b>%s</b> ] Can't include class.secure.php!", $_SERVER["SCRIPT_NAME"]), E_USER_ERROR);
	}
}
// end include class.secure.php

class quickform extends LEPTON_abstract {

	// custom class
	public $oQCFE;

	// set default language code
	public $default_language = "en";

	// private cache for the evaluated page language code of current page
	private $page_language;

	/**
	 *	The reference to *Singleton* instance of this class
	 *
	 *	@var	object
	 *	@access	private
	 *
	 */
	public static $instance;

	public function initialize()
	{
	}

	public function __construct($section_id = 0)
	{
	}

	/** =========================================================================
	 *	Get a captcha
	 */
	public function captcha($section_id = 0)
	{
		if(file_exists( LEPTON_PATH."/modules/quickform/recaptcha.php" ))
		{
			require_once LEPTON_PATH."/modules/quickform/recaptcha.php";
			return quickform_recaptcha::build_captcha();
		}

		require_once(LEPTON_PATH."/modules/captcha_control/captcha/captcha.php");
		ob_start();
			call_captcha("all","",$section_id);
			$captcha = ob_get_clean();
		return $captcha;
	}

	/** =========================================================================
	 *
	 * get quickform settings
	 *
	 * @param	int			A valid section id
	 * @return	Nothing
	 *
	 */
	public function get_settings( $section_id )
	{
		global $database;
		$result = array();
		$database->execute_query(
			"SELECT * FROM `".TABLE_PREFIX."mod_quickform` WHERE `section_id` = ".$section_id,
			true,
			$result,
			false
		);

		// add hardcoded values
		if ( array_key_exists("max_email_split", $result ) === false )
		{
			$result["max_email_split"] = 3;
		}

		return $result;
	}

	/** =========================================================================
	 *
	 * get page language
	 *
	 * @param	int			A valid page id
	 * @return	string		the page language
	 *
	 */
	public function get_page_language( $page_id )
	{
		if ( empty( $this->page_language ))
		{
			global $database;
			$this->page_language = $database->get_one("SELECT `language` FROM `".TABLE_PREFIX."pages` WHERE `page_id`=".(int)$page_id);
			$this->page_language = strtolower( $this->page_language );

			if ( empty( $this->page_language ))	{ $this->page_language = $this->default_language; }
		}
		return $this->page_language;
	}

	/** =========================================================================
	 *
	 * get template including proper language as folder
	 *
	 * @param	int			A valid page id
	 * @param	string		the base path
	 * @param	string		a specific language
	 * @param	string		the template name
	 * @return	boolean		true if template has been found, false otherwise
	 *
	 */
	public function get_template( $page_id, &$template_path, &$template_lang, $template_name )
	{
		// set languages to be checked
		if ( $template_lang === "" )
		{
			$page_language = $this->get_page_language( $page_id );
			$languages = array( $page_language, $this->default_language );
		} else {
			$languages = array( $template_lang );
		}

		// validate
		if (strpos( $template_path, DIRECTORY_SEPARATOR . "templates") === false )
		{
			$template_path .= DIRECTORY_SEPARATOR . "templates";
		}
		$template_path .= DIRECTORY_SEPARATOR;
		$template_path = str_replace(array( "\\", "/", DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR ), DIRECTORY_SEPARATOR, $template_path);

		// check for template path
		$result = false;
		foreach( $languages as $template_lang )
		{
			if ( file_exists( $template_path . $template_lang . DIRECTORY_SEPARATOR . $template_name ))
			{
				$result = true;
				break;
			}
		}

		return $result;
	}

	/** =========================================================================
	 *
	 * get data fields out of a template, which could be submitted (field name start with "qf_")
	 *
	 * @param	string		the full path qualified template
	 * @return	array		an array with data fields out of the template
	 *
	 */
	public function get_template_fields( $template_file, $honeypotfield = NULL )
	{
		// get the template file content
		// $template_string = file_get_contents( $template_file );
		global $section_id;

		$template_values = array(
            "WEBSITE_TITLE"     => WEBSITE_TITLE,
            "LEPTON_URL"		=> LEPTON_URL,
            "MOD_QUICKFORM"     => $this->language,
            "HONEYPOT_FIELD"	=> $honeypotfield,
            "CAPTCHA"			=> $this->captcha( $section_id )//,
            // "MESSAGE"           => ""   // Aldus: 2021-01-03 - to avoid warnings for arrayToString conversations within xDebug
		);
		$oTWIG = lib_twig_box::getInstance();
		$template_string = $oTWIG->render(
			 "@quickform/" . $template_file
			,$template_values
		);

		// find all field rows inside the template
		$fields = array();
		$matches = array();
		preg_match_all(
		    '/<input [^>]*>|<select [^>]*>|<textarea [^>]*>/iU',
		    $template_string,
		    $matches
		);

		// find all fields per field row, identified by name
		foreach( $matches[0] as $match )
		{
			if ( preg_match('/name="([^"]*)"/i', $match, $field ))
			{
				$items = array();

				$items["name"] = str_replace("[]","",$field[1]);
				$items["submit"] = false;
				if ( strpos( $items["name"], "qf_" ) !== false )	{ $items["submit"] = true; }
				$items["required"] = false;
				if ( strpos( $items["name"], "_r_" ) !== false )	{ $items["required"] = true; }

				// set id
				$id = strtolower( $items["name"] );
				$id = str_replace( "qf_r_", "", $id );
				$id = str_replace( "qf_", "", $id );
				$items["id"] = trim( strtoupper( $id ));

				// initialize the field value, will be set in view.php later on in process
				$items["value"] = "";

				// get type
				$items["type"] = "";
				if ( strtolower(Substr( $match, 0, StrLen("<input"))) == "<input" )
				{
					if ( preg_match('/type="([^"]*)"/i', $match, $type ))
					{
						$items["type"] = strtoupper( str_replace("[]","",$type[1]) );
					}
				}
				elseif ( strtolower(Substr( $match, 0, StrLen("<select"))) == "<select" )
					{ $items["type"] = "SELECT"; }
				elseif ( strtolower(Substr( $match, 0, StrLen("<textarea"))) == "<textarea" )
					{ $items["type"] = "TEXTAREA"; }

				// check if a data-label attribute exists in form field definition
				$items["data-label"] = $items["id"];
				// used only in mail
				if ( preg_match('/data-label="([^"]*)"/i', $match, $label ))
				{
					$items["data-label"] = str_replace("[]","",$label[1]);
				}

				// check if a data-value attribute exists in form field definition
				// used only in mail
				if ( preg_match('/data-value="([^"]*)"/i', $match, $label ))
				{
					$items["data-value"] = str_replace("[]","",$label[1]);
				}

				// update array
				$fields[ $items["name"]] = $items;
			}
		}

		return $fields;
	}

	/** =========================================================================
	 *
	 * get next message id following currently moved or deleted message id for anchor target
	 *
	 * @param	int			A valid section id
	 * @param	int			A valid message id or string "all"
	 * @param	char		A group code where messages are inside
	 * @return	Nothing
	 *
	 */
	public function get_anchor_target( $section_id, $msgid, $grpcur )
	{
		// validate
		if ( empty($msgid))		{ return "groups"; }
		if ( $msgid == "all" )	{ return $msgid; }
		$msgid = intval( $msgid );
		if ( ! $msgid > 0 )		{ return "groups"; }

		// execute
		global $database;
		$result = $database->get_one(
			  "SELECT MIN(`message_id`)"
			. " FROM `".TABLE_PREFIX."mod_quickform_data`"
			. " WHERE `section_id` = '".$section_id."'"
		    . "   AND `msg_group`  = '".$grpcur."'"
		    . "   AND `message_id` > '".$msgid."'"
			. " ORDER BY `message_id` desc"
			. ";"
			);

		if ( empty($result))	{ $result = "groups"; }
		return $result;
	}

	/** =========================================================================
	 *
	 * move message(s) from one message collection into another
	 *
	 * @param	int			A valid section id
	 * @param	variant		A valid message id or string "all"
	 * @param	char		A group code where messages are inside
	 * @param	char		A group code as target
	 * @param	int			Number of max rows (optional, required when move all)
	 * @return	Nothing
	 *
	 */
	public function move_msg( $section_id, $msgid, $grpcur, $grpnew, $rowcur )
	{
		// validate
		if ( empty($msgid))		{ return -1; }
		if ( empty($grpcur))	{ return -2; }
		if ( empty($grpnew))	{ return -3; }
		if ( $msgid == "all" )
		{
			if (( empty($rowcur) ) || !( $rowcur > 0 )) { return -4; }
		}
		else
		{
			$msgid = intval( $msgid );
			if ( ! $msgid > 0 ) { return -5; }
		}

		// execute
		global $database;
		$result = array();
		$sql = "UPDATE"
			. " `".TABLE_PREFIX."mod_quickform_data`"
			. " SET `msg_group` = '".$grpnew."'"
			. " WHERE `section_id` = '".$section_id."'";

		if ( $msgid == "all" )
		{
			$sql .= "   AND `msg_group` = '".$grpcur."'"
				  . " ORDER BY `message_id` desc"
				  . " LIMIT ".$rowcur;
		}
		else
		{
			$sql .= "   AND `message_id` = '".$msgid."'"
				  . "   AND `msg_group`  = '".$grpcur."'";
		}
		$sql .= ";";
		$result = $database->simple_query( $sql );
		return $result;
	}

	/** =========================================================================
	 *
	 * delete message(s)
	 *
	 * @param	int			A valid section id
	 * @param	variant		A valid message id or string "all"
	 * @param	char		A group code where messages are inside
	 * @param	int			Number of max rows (optional, required when delete all)
	 * @return	Nothing
	 *
	 */
	public function delete_msg( $section_id, $msgid, $grpcur, $rowcur )
	{
		// validate
		if ( empty($msgid))		{ return -1; }
		if ( empty($grpcur))	{ return -2; }
		if ( $msgid == "all" )
		{
			if (( empty($rowcur) ) || !( $rowcur > 0 )) { return -3; }
		}
		else
		{
			$msgid = intval( $msgid );
			if ( ! $msgid > 0 ) { return -4; }
		}

		// execute
		global $database;
		$result = array();
		$sql = "DELETE FROM"
			. " `".TABLE_PREFIX."mod_quickform_data`"
			. " WHERE `section_id` = '".$section_id."'";

		if ( $msgid == "all" )
		{
			$sql .= "   AND `msg_group` = '".$grpcur."'"
				  . " ORDER BY `message_id` desc"
				  . " LIMIT ".$rowcur;
		}
		else
		{
			$sql .= "   AND `message_id` = '".$msgid."'"
				  . "   AND `msg_group`  = '".$grpcur."'";
		}
		$sql .= ";";
		$result = $database->simple_query( $sql );
		return;
	}

	/** =========================================================================
	 *
	 * save number of items to be shown
	 *
	 * @param	int			A valid section id
	 * @param	int			New Number of max rows
	 * @return	Nothing
	 *
	 */
	public function set_rows( $section_id, $rowcur )
	{
		global $database;
		$result = array();
		$sql = "UPDATE "
			. " `".TABLE_PREFIX."mod_quickform`"
			. " SET `usenbritems` = ".$rowcur
			. " WHERE `section_id` = '".$section_id."'"
			.  ";";
		$result = $database->simple_query( $sql );
		return;
	}

	/** =========================================================================
	 *
	 * set new view to be used
	 *
	 * @param	int			A valid section id
	 * @param	char		New view name
	 * @return	Nothing
	 *
	 */
	public function change_view( $section_id, $useview )
	{
		global $database;
		$result = array();
		$sql = "UPDATE "
			. " `".TABLE_PREFIX."mod_quickform`"
			. " SET `useview` = '".$useview."'"
			. " WHERE `section_id` = '".$section_id."'"
			.  ";";
		$result = $database->simple_query( $sql );
		return;
	}

	/** =========================================================================
	 *
	 * Get the submissions of this section
	 *
	 * @param	int			A valid section id
	 * @param	char		A group code where messages are inside
	 * @param	int			Optional number of max rows
	 * @return	array		A linear array within assoc. subarray
	 *
	 */
	public function get_history ( $section_id, $grpcur, $rowcur, $viewcur )
	{

		$database = LEPTON_database::getInstance();
		$result = array();
		$database->execute_query(
			  "SELECT *"
			. " FROM `".TABLE_PREFIX."mod_quickform_data`"
			. " WHERE `section_id` = '".$section_id."'"
		    . "   AND `msg_group`  = '".$grpcur."'"
			. " ORDER BY `message_id` desc"
			. " LIMIT 0,".$rowcur.";",
			true,
			$result,
			true
		);

		// get the single elements out of the mail. Do not do in CLASSIC view as it is not used therein
		if ( $viewcur !== "CLASSIC" )
		{
			foreach( $result as &$item )	// loop mails
			{
				// initialize the prepared field content array
				$fields = array();

				/*** create a new dom object ***/
				$dom = new domDocument;

				/*** discard white space ***/
				$dom->preserveWhiteSpace = false;
				$dom->formatOutput = false;

				/*** load the html mail content into the object ***/
				$dom->loadHTML( $item[ "data" ] );
				$xpath = new DOMXPath($dom);

				if (strpos( $item[ "data" ], "<table id='emailcontent'"))
				{
					$domcols = $xpath->query('//table[@id="emailcontent"]/tr/td');
				}
				else	// old emails
				{
					/*** load the html mail content into the object ***/
					$domcols = $xpath->query('.//table[last()]/tr/td');
				}

				/*** loop over the table columns ***/
				$i = 1;
				foreach ($domcols as $domcol)
				{
					// first col is header, second the value
					if (( $i == 1 ) && ( substr_count($domcol->nodeValue, ':') == 1 ))
					{
						$key = $domcol->nodeValue;
						$i = 2;
					}
					elseif ( $i == 2 )
					{
                        // Aldus 2020-12-09: Umlauts in the Message
						$fields[ $key ] = utf8_decode($domcol->nodeValue);
						$i = 1;
					}
				}
				unset($dom);

				// ignore because it contains only the current section id
				if ( array_key_exists( "QUICKFORM" , $fields )){ unset( $fields["QUICKFORM"] ); }

				// handle exception in case no data can be evaluated
				if ( count( $fields) == 0 )
				{
					$fields['MAIL SUBMIT'] = date( DATE_FORMAT.'  '.TIME_FORMAT, $item['submitted_when'] );
				}

				// update array
				$item[ "msg_data" ] = $fields;
			}
		}

		return $result;
	}

	/** =========================================================================
	 *
	 * Get the available groups
	 *
	 * @param	int		A valid section id
	 * @return	array	A linear array within assoc. subarray
	 *
	 */
	public function get_groups( $section_id )
	{

		$database = LEPTON_database::getInstance();
		$result = array();
		$database->execute_query(
			  "SELECT DISTINCT `msg_group`, COUNT(*) AS `nbr_items`"
			. " FROM `".TABLE_PREFIX."mod_quickform_data`"
			. " WHERE `section_id` = '".$section_id."'"
			. " GROUP BY `msg_group`",
			true,
			$result,
			true
		);

		return $result;
	}

	/** =========================================================================
	 *
	 * Create a set of messages for testing purposes
	 *
	 * @param	int			A valid section id
	 * @param	char		A group code where messages are inside
	 * @param	int			number of max rows
	 * @return	nothing
	 *
	 */
	public function clone_messages ( $section_id, $grpcur, $rowcur )
	{
		// get one row as base
		$database = LEPTON_database::getInstance();
		$result = array();
		$database->execute_query(
			  "SELECT *"
			. " FROM `".TABLE_PREFIX."mod_quickform_data`"
			. " WHERE `section_id` = '".$section_id."'"
		    . "   AND `msg_group`  = '".$grpcur."'"
			. " ORDER BY `message_id` desc"
			. " LIMIT 0,1;",
			true,
			$result,
			true
		);

		// remove auto increment field
		foreach( $result as $row )
		{
			$row[ "message_id" ] = NULL;

			// create number of clones
			for ($x = 1; $x <= $rowcur; $x++)
			{
				 $result = $database->build_and_execute(
						"insert",
						TABLE_PREFIX."mod_quickform_data",
						$row
					);
			}
		}

		return $result;
	}

	/** =========================================================================
	 *
	 * Build page list
	 *
	 * @param	int		A valid parent page id
	 * @param	int		Current page_id
	 * @return	array	A assoc. array
	 *
	 */
	public function build_pagelist( $parent, $this_page )
	{
		global $database, $pages;
		$iterated_parents = array(); // keep count of already iterated parents to prevent duplicates

		$data = array();
		$database->execute_query(
			  "SELECT link, menu_title, page_title, page_id, level "
			. " FROM `".TABLE_PREFIX."pages`"
			. " WHERE `parent` = ".$parent
			. " ORDER BY level, position ASC"
			,true
			,$data
			,true
		);

		if ( count($data) > 0 )
		{
			foreach( $data as $res )
			{
				$pages[$res["page_id"]] = str_repeat("  -  ", $res["level"]) . $res["menu_title"] . " (" . $res["page_title"] . ")";
				if (!in_array($res["page_id"], $iterated_parents))
				{
					$this->build_pagelist($res["page_id"], $this_page);
					$iterated_parents[] = $res["page_id"];
				}
			}
		}
	}

	/** =========================================================================
	 *
	 * Show an info popup
	 *
	 * @access  public
	 * @param   $modvalues  As optional array containing module specialized values
	 * @param   $bPrompt    True for direct output via echo, false for returning the generated source.
	 * @return  mixed       Depending on the $bPrompt param: boolean or string.
	 */
	public function showmodinfo( $modvalues = null, $bPrompt = true )
	{
		// prepare array with module specific values
		$modvalues = array(
			 "IMAGE_URL"	=> str_replace( LEPTON_PATH, LEPTON_URL, __DIR__ ) . "/../images/quickform.png"
			,"BUTTONS"		=> array(
				 "HELP"		=> array( "AVAILABLE"	=> true
										,"URL"		=> "https://doc.lepton-cms.org/docu/english/tutorials/doc-quickform.php"
				)
			)
		);

		// show module info
		$sSource = parent::showmodinfo( $modvalues );
		
		return $sSource;
	}

	/** =========================================================================
	 *
	 * send the mail
	 *
	 * @param	array		template fields and values
	 * @param	string		mail receiver ident
	 * @param	array		quickform settingss
	 * @param	array	&	email parameter
	 * @return	boolean		true if any error occured, false otherwise
	 */
	public function mail_send( $template_fields, $receiver_ident, $settings, &$email_params )
	{
		// prepare the email settings
		$isError = false;
		$email_params = array();

		// prepare mail with receivers, text, etc
		$isError = $this->mail_prepare( $template_fields, $receiver_ident, $settings, $email_params );

		// an error had occured during the preparation of the email parameters
		if ( true === $isError )
		{
			Return $isError;
		}

		// no email is needed
		if ( $email_params[ "mail2receiver" ] === false )
		{
			Return false;	// No error
		}

		// create mail instance
		$qfMail = LEPTON_mailer::getInstance();
		$qfMail->CharSet	= "UTF-8";

		// set Sender
		$qfMail->setFrom( $email_params[ "sender" ][ "from"], $email_params[ "sender" ][ "name"] );

		// set Recipients
		foreach ( $email_params[ "receiver" ] as $email )
		{
			$qfMail->AddAddress( $email[ "from"], $email[ "name"] );
		}
		// $qfMail->addReplyTo(	"info@example.com", "Information");
		// $qfMail->addCC("cc@example.com");
		// $qfMail->addBCC("bcc@example.com");

		// set Content
		$qfMail->isHTML( $email_params[ "isHTML" ] );
		$qfMail->Subject	= $email_params[ "subject" ];
		$qfMail->Body		= $email_params[ "message" ];
		$qfMail->AltBody	= $email_params[ "message_textonly" ];

		// send mail, then check if there are any send mail errors, otherwise say successful
		if ( ! $qfMail->Send())
		{
			$email_params[ "error" ] = array("ERROR_SENDMAIL", $qfMail->ErrorInfo );
			return true;
		}
		$qfMail = "";

		return false;	// No error
	}

	/** =========================================================================
	 *
	 * prepare the mail and validate the input
	 *
	 * @param	array		template fields and values
	 * @param	string		mail receiver ident
	 * @param	array		quickform settingss
	 * @param	array	&	email parameter
	 * @return	boolean		true if any error occured, false otherwise
	 */
	public function mail_prepare( $template_fields, $receiver_ident, $settings, &$email_params )
	{
		// define subject
		$email_params[ "subject" ] = $settings["subject"];

		// define sender mail address
		$email_params[ "sender" ] = array();
		if ( ! empty( SERVER_EMAIL ))
		{
			$email_params[ "sender" ] = array( "from" => SERVER_EMAIL, "name" => MAILER_DEFAULT_SENDERNAME );
		}
		if ( count( $email_params[ "sender" ] ) == 0 )
		{
			$email_params[ "error" ] = "ERROR_SENDER_MISSED";
			return true;
		}

		// evaluate receiver mail address(es)
		$email_params[ "receiver" ] = array();
		$email_params[ "mail2receiver" ] = false;
		switch( $receiver_ident )
		{
			case "quickform":
				if ( ! empty( $settings["email"] ))
				{
					$email_params[ "receiver" ] = $this->mail_split_address( $settings["email"], $email_params );
				}
				if ( count( $email_params[ "receiver" ] ) == 0 )
				{
					// missing mail receiver together with CC_MAIL setup
					$email_params[ "error" ] = "ERROR_RECEIVER_MISSED";
					return true;
				}
				$email_params[ "mail2receiver" ] = true;
				break;

			case "requestor":
				// requires both the CC_MAIL form field with non empty value
				// and at least one valid email address
				foreach( $template_fields as $key=>$items )
				{
					if ( substr( $items["id"], 0, 5 ) == "EMAIL" )
					{
						$email_params[ "receiver" ] = $this->mail_split_address( $items["value"], $email_params );
					}
					elseif (( $items["id"] == "CC_MAIL" ) && ( $items["value"] != "" ) && ( $items["value"] != "-" ))
					{
						$email_params[ "mail2receiver" ] = true;
					}
				}
				if (( false === $email_params[ "mail2receiver" ]) || ( count( $email_params[ "receiver" ] ) == 0 ))
				{
					// no CC_MAIL is requested or no mail addresses are defined: No error
					$email_params[ "mail2receiver" ] = false;
					return false;
				}
				break;
		};

		// find mail template
		$email_params[ "template_name" ] = "";
			// get page language (has been set earlier)
			$page_language = $this->get_page_language( 0 );

			// build array with possible file names
			$templates = array();
			switch( $receiver_ident )
			{
				case "requestor":
					$templates[] = "cc_mail";
				case "quickform":
					$templates[] = "email";	// add to array for requestor and for quickform
			};
			$tempate_files = array();
			foreach ( $templates as $template )
			{
				$tempate_files[] = $template . "_" . $page_language . ".lte";
				$tempate_files[] = $template . "_" . $this->default_language . ".lte";
				$tempate_files[] = $template . ".lte";
			}

			// add default mail template as fallback
			$tempate_files[] = "qf_email.lte";

			// search for template
			$template_path = __DIR__ . DIRECTORY_SEPARATOR . ".." . DIRECTORY_SEPARATOR;
			$template_lang = "backend";
			foreach ( $tempate_files as $template_name )
			{
				if ( true === $this->get_template( 0, $template_path, $template_lang, $template_name ) )
				{
					$email_params[ "template_name" ] = $template_name;
					break;		// stop on first template found
				}
			}
		if ( empty( $email_params[ "template_name" ] ))
		{
			$email_params[ "error" ] = "ERROR_MAIL_MISSED";
			return true;
		}

		// build mail field and values list
		$isTimestampKey = '';
		$field_values = array();
		foreach( $template_fields as $key => $items )
		{
			// check for timestamp
			if ( $items[ "id" ] == "TIMESTAMP" )
			{
				$items[ "submit" ] = true;
			}

			// field is a submit field (name start with "qf_")
			if ( $items[ "submit" ] === true )
			{
				if ( array_key_exists( "data-value", $items ))
				{
					$field_values[ $items["data-label"] ] = $items["data-value"];
				} else {
					$field_values[ $items["data-label"] ] = $items["value"];
				}

				// format value
				if ( $items[ "id" ] == "TIMESTAMP" )
				{
					if ( False !== ( $value = @strftime('%c', $field_values[ $items["data-label"] ] ) ))
					{
						$field_values[ $items["data-label"] ] = $value;
					}
				}
			}
		}

		// set initial template values
		$template_values = array(
				 "MOD_QUICKFORM"	=> $this->language
				,"field_values"		=> $field_values
				,"settings"			=> $settings
		);

		// enhance template values by custom class 
		if ( is_object($this->oQCFE) )
		{
			if (( method_exists( $this->oQCFE, 'set_mail_values' ))
			&&  ( is_callable( array($this->oQCFE, 'set_mail_values' )) ))
			{
				$this->oQCFE->set_mail_values( $template_fields, $template_values );
			}
		}

		// get prepared mail text
		$oTWIG = lib_twig_box::getInstance();
		$email_params[ "message" ] = $oTWIG->render(
			 "@quickform/" . $template_lang . DIRECTORY_SEPARATOR . $email_params[ "template_name" ]
			,$template_values
		);

		$email_params[ "message_textonly" ] = "";
		$email_params[ "isHTML" ] = false;

		// prepare alternate text (remove html tags)
		$textbody = strip_tags( $email_params[ "message" ]);
		$textbody = str_replace("\t","",$textbody);
		while (strpos($textbody,"\n\n\n") !== false)
			$textbody = str_replace("\n\n\n","\n\n",$textbody);
		while (strpos($textbody,"\r\n\r\n\r\n") !== false)
			$textbody = str_replace("\r\n\r\n\r\n","\r\n\r\n",$textbody);

		if ( $textbody != $email_params[ "message" ] )
		{
			$email_params[ "message_textonly" ] = $textbody;
			$email_params[ "isHTML" ] = true;
		}

		return false;	// no error
	}

	/** =========================================================================
	 *
	 * split the email address if needed
	 *
	 * @param	string		email address
	 * @param	array		already available email addresses
	 * @return	array		enhanced array with input email address(es)
	 */
	private function mail_split_address( $mail_address, $email_params )
	{
		$mail_receivers = $email_params[ "receiver" ];
		$addresses = array();
		// email address is a form array
		if ( is_array( $mail_address ) === true )
		{
			foreach ( $mail_address as $key => $value )
			{
				$addresses[] = $value;
			}
		}
		// email address is a single address
		elseif ( strpbrk ( $mail_address , ",; " ) === false )
		{
			$addresses[] = $mail_address;
		}
		// split address by first delimiter char found
		// but ignore empty elements like in "mail_1,,mail_2"
		else
		{
			foreach( array( ",", ";", " ") as $delimiter )
			{
				if ( strpos ( $mail_address , $delimiter ) > 0 )
				{
					$addresses = array_filter( explode ( $delimiter, $mail_address ), "strlen" );
					break;	// foreach delimiter loop
				}
			}
			// only defined number of split elements are allowed in order to protect against spam
			if ( count( $addresses ) > $email_params[ "max_email_split" ] )
			{
				$addresses = array_slice( $addresses, 0, $email_params[ "max_email_split" ]);
			}
		}

		if ( count( $addresses ) > 0 )
		{
			foreach( $addresses as $key => $val )
			{
				// use only valid email addresses
				if ( filter_var( $val, FILTER_VALIDATE_EMAIL ))
				{
					$mail_receivers[ $val ] = array( "from" => $val, "name" => "" );
				}
			}
		}

		return $mail_receivers;
	}

	/** =========================================================================
	 *
	 * honeypot check
	 *
	 * @param	string		one of the actions show or save
	 * @param	string		sha1( $_SERVER['REMOTE_ADDR'] )
	 * @param	string		sha1( $_SERVER['HTTP_USER_AGENT'] );
	 * @param	array		the quickform settings
	 * @param	array		page values
	 * @param	number		form load time, used for entry time check
	 * @param	string		the name of the currently used honeypot field
	 * @param	number		the index of the currently used honeypot field
	 * @return	array		return status
	 *						 1 = success
	 *						 0 = no check executed
	 *						-1 = failure: too many sessions within 1 hour
	 *						-2 = failure: no temp file row available
	 *						-3 = failure: invalid timestamps, form vs. temp table
	 *						-4 = failure: too fast data entry
	 *						-5 = failure: invalid honeypot index
	 *						-6 = failure: invalid honeypot field
	 *						-7 = failure: honeypot field contains a value
	 */
	public function spam_check( $action
								, $ip_fingerprint
								, $browser_fingerprint
								, $settings
								, $page_values
								, $pageloadtime
								, $honeypotfield
								, $honeypotindex
								)
	{
		global $database;
		$return = 1;
		$info = array();

		// delete old rows older than 1 hour
		$time = $pageloadtime - 3600;
		$database->simple_query("DELETE from `".TABLE_PREFIX."temp` WHERE `temp_time` < '" . $time . "' and `temp_ip` like 'qf_%'");

		// no further actions if honeypot functionality is switched off
		if ( $settings["use_honeypot"] != 1 )
		{
			return 0;
		}

		// ... get available temp table row
		$database->execute_query(
			"SELECT * FROM `".TABLE_PREFIX."temp` WHERE `temp_ip` = 'qf_".$ip_fingerprint."'",
			true,
			$info,
			false
		);

		// reaction depending on action ...
		switch( $action )
		{
			// when action = show, ...
			case "show":
				// ... any active row exists should be older than 1 hour
/*
				// Problem: This failure occurs also with a simple refresh ....
				// So let's forget for the moment ;-)
				if (( count($info) > 0 ) && ( 1 == $info['temp_active'] ) && ( $info['temp_time'] > $time ))
				{
					$return = -1;
					break;	// end with error
				}
*/
				// ... remove existing temp table row
				$database->simple_query("DELETE from `".TABLE_PREFIX."temp` WHERE `temp_ip` = 'qf_".$ip_fingerprint."'");

				// ... create new temp table row
				$fields = array(
					 'temp_ip'		=> "qf_".$ip_fingerprint
					,'temp_browser'	=> "qf_".$browser_fingerprint
					,'temp_time'	=> $page_values["TIMESTAMP"]
					,'temp_count'	=> 1
					,'temp_active'	=> 1
				);
				$database->build_and_execute(
					'insert',
					TABLE_PREFIX."temp",
					$fields
				);

				break;	// end successfull

			// when action = save, ...
			case "save":
				// ... trigger failure when no row exists
				if ( 0 === count($info))
				{
					$return = -2;
					break;	// end with error
				}

				// ... check saved time and the form time
				if ( (int)$info['temp_time'] != (int)$page_values["TIMESTAMP"] )
				{
					$return = -3;
					break;	// end with error
				}

				// ... check time used for data entry, fail if too fast
				if ( (int)$settings['spam_checktime'] > 0 )
				{
					if ( (int)$pageloadtime - (int)$info['temp_time'] < (int)$settings['spam_checktime'] )
					{
						$return = -4;
						break;	// end with error
					}
				}

				// no honeypot fields defined, no further checks
				if ( ! Trim( $settings["spam_honeypot"] ))
				{
					break;	// end without error
				}

				// check honeypot index
				if ( (int)$honeypotindex >= 0 )
				{
					$honeypots = explode( ",", $settings["spam_honeypot"] );	// array of honeypot fields
					$indexfield = trim( $honeypots[ (int)$honeypotindex ] );	// currently used honeypot field
					if ( ! $indexfield )
					{
						$return = -5;
						break;	// end with error
					}
				}
				else
				{
					$return = -5;
					break;	// end with error
				}

				// check honeypot field
				if (( ! Trim( $honeypotfield )) || ( strtolower(trim($indexfield)) != strtolower(trim($honeypotfield)) ))
				{
					$return = -6;
					break;	// end with error
				}

				// check honeypot value
				if ( trim( $page_values[ strtoupper( $honeypotfield ) ] ) != "" )
				{
					$return = -7;
					break;	// end with error
				}

				// no spam, remove existing temp table row
				$database->simple_query("DELETE from `".TABLE_PREFIX."temp` WHERE `temp_ip` = 'qf_".$ip_fingerprint."'");

				break;	// end successfull
		}	// end switch

		// increase tempt table counter in case of error
		if (( $return < 0 ) && ( 1 === count($info)) && ( $settings['spam_logging'] == 1 ))
		{
			$fields = array(
				 'temp_count' => $info['temp_count'] + 1
				,'temp_time'  => time()
			);

			$database->build_and_execute(
				'update',
				TABLE_PREFIX."temp",
				$fields,
				"`temp_id`='qf_".$info['temp_id']."'"
			);
		}

		// return with spam check result
		return $return;
	}

} //end class
