<?php

/**
 * This file is part of LEPTON Core, released under the GNU GPL
 * Please see LICENSE and COPYING files in your package for details, specially for terms and warranties.
 * 
 * NOTICE:LEPTON CMS Package has several different licenses.
 * Please see the individual license in the header of each single file or info.php of modules and templates.
 *
 * @author          LEPTON Project
 * @copyright       2010-2022 LEPTON Project
 * @link            https://lepton-cms.org
 * @license         http://www.gnu.org/licenses/gpl.html
 * @license_terms   please see LICENSE and COPYING files in your package
 *
 */


/**
 *	Class to handle values out the $_POST or $_GET
 */
 
class LEPTON_request extends LEPTON_abstract
{

        static $instance;

	/**
         *  Public var to handle the way the class should look for the value;
         *  supported types are 'post', 'get' or 'request'
         *
         *  @type       string
         *  @default    "post"
         *
         */
        public $strict_looking_inside = "post";

        /**
         *  By default we are not collecting any errors
         *
         *  @since      0.1.0
         *  @type       bool
         *  @default    false
         *
         */
        public $error_mode = false;

        /**
         *  Public var that holds the errors in an array.
         *
         *  @since      0.1.0
         *  @type       array
         *  @default    empty array
         *
         */
        public $errors = array();

        public function initialize()
        {
            // comes from parent
        }

        /**
         *  Testing a list of values against the $_POST array and returns a linear list of the results.
         *
         *  @param  array   An array with an assoc. subarray for the 'keys' to test for.
         *
         *  @code{.php}
         *  // a simple list of values to test agains $_POST
         *  $aLookUp = array(
         *       'page_id'      => array('type' => 'integer+', 'default' => NULL),
         *       'section_id'   => array('type' => 'integer+', 'default' => NULL),
         *       'hello_text'   => array('type' => 'string', 'default' => ''),
         *       'content'      => array('type' => 'string_chars', 'default' => ""),
         *       'text'         => array('type' => 'string_clean', 'default' => ""),
         *       'input'        => array('type' => 'string_allowed', 'default' => "", 'range' => array('h1','h2','h3') )
         *   );
         *
         *  @endcode
         *
         *  @return array   The results as an assoc. array.
         *
         */
        public function testPostValues( &$aValueList )
        {
            $this->strict_looking_inside = "post";
            
            $aReturnList = array();
            foreach($aValueList as $term => $options)
            {
                $aReturnList[ $term ] = $this->get_request( 
                    $term, 
                    $options['default'], 
                    $options['type'], 
                    (isset($options['range']) ? $options['range'] : "" )
                );
            }
            
            return $aReturnList;
        }
        
        /**
         *  Testing a list of values against the $_GET array and returns a linear list of the results.
         *  This method is similar to ::testPostValues
         *
         *  @param  array   An array with an assoc. subarray for the 'keys' to test for.
         *
         *  @code{.php}
         *  // a simple list of values to test agains $_GET
         *  $aLookUp = array(
         *       'page_id'      => array('type' => 'integer+', 'default' => NULL),
         *       'section_id'   => array('type' => 'integer+', 'default' => NULL),
         *       'hello_text'   => array('type' => 'string', 'default' => ''),
         *       'content'      => array('type' => 'string_chars', 'default' => ""),
         *       'text'         => array('type' => 'string_clean', 'default' => ""),
         *       'input'        => array('type' => 'string_allowed', 'default' => "", 'range' => array('h1','h2','h3') )
         *   );
         *
         *  @endcode
         *
         *  @return array   The results as an assoc. array.
         *
         */
        public function testGetValues( &$aValueList )
        {
            $this->strict_looking_inside = "get";
            
            $aReturnList = array();
            foreach($aValueList as $term => $options)
            {
                $aReturnList[ $term ] = $this->get_request(
                    $term,
                    $options['default'],
                    $options['type'],
                    (isset($options['range']) ? $options['range'] : "" )
                );
            }
            
            return $aReturnList;
        }

        /**
         *
         *  Public function that looks for a value in the $_POST or $_GET super-variables.
         *  If it is found and the type match it is returned. If not, the given default
         *  value is returned. There are also optional settings for the situation, the
         *  value has to be in a given range, and/or the string has to be a given size.
         *
         *  @param  string  name    Name of the value/key
         *  @param  mixed   default (optional) The default of this value
         *  @param  string  type    (optional) The type of the value, see list below
         *  @param  array   range   (optional) The settings for testing if the value is inside a range.
         *
         *  @retuns mixed   the value
         *
         *  NOTICE: the range-settings have no effekt if the default-value is set to NULL.
         *
         *
         *  Supported types are (quick-, short-, long-notation)
         *
         *  integer     Any integer
         *  integer+    Any positive integer +0
         *  integer-    Any negative integer +0
         *
         *  string      Any string
         *  string_clean    Any string - HTML tags will be removed!
         *  string_chars    Any String - but tags are converted via htmlspecialchars!
         *  string_allowed  Any String - HTML tags are removed, exept the given one in "range"!
         *
         *  email   Any mail adress
         *  array   Any array (e.g. from a multible select)
         *
         *  Range got following keys/options
         *
         *      min     the minmium, as for integers the lowest number, as for strings the min. number of chars.
         *              default: 0;
         *
         *      max     the maximum, as for integers the heights number, as for strings the max. number of chars.
         *              default: 255;
         *
         *      use     what kind of use if the value is not inside the range, supported types are
         *              - default   use the given default value (integer, string, NULL)
         *              - min       however (value is less or more out of the range) use the min-value.
         *              - max       however (value is less or more out of the range) use the max-value.
         *              - near      if the value is less, use the min-value, if it is more, use the max-value.
         *              - fill      only for strings.
         *              - cut       only for strings.
         *                          default: 'default'
         *
         *      char    default: ' 'if #fill is used and the number of chars is less than the min-value,
         *              the string is 'filled' up with this char.
         *              default: ' ' (space|blank)
         *
         *  If one of the keys/options is missing, the default-settings are used.
         *  NOTICE: the range-settings have no effekt if the default-value is set to NULL.
         *
         */

	public function get_request( $aName="", $aDefault=NULL, $type="", $range="")
        {
		
		if ($aName == "")
		{
		    return NULL;
		}
		
		if ( false === $this->strict_looking_inside ) {
		
			if ( strtoupper($_SERVER['REQUEST_METHOD']) == "POST") {
				$return_value = (true === array_key_exists ($aName , $_POST) ) ? $_POST[$aName] : $aDefault;
			} else {
				$return_value = (true === array_key_exists ($aName , $_GET) ) ? $_GET[$aName] : $aDefault;
			}
		} else {
			switch (strtolower($this->strict_looking_inside)) {
				case 'post':
					$return_value = (true === array_key_exists($aName , $_POST) ) ? $_POST[$aName] : $aDefault;
					break;
					
				case 'get':
					$return_value = (true === array_key_exists($aName , $_GET) ) ? $_GET[$aName] : $aDefault;
					break;
					
				case 'request':
					$return_value = (true === array_key_exists($aName , $_REQUEST) ) ? $_REQUEST[$aName] : $aDefault;
					break;
					
				default:
					$return_value = NULL;
					break;
			}
		}
		
		if ($type != "") {
			switch (strtolower($type)) {
				//  [1] Integers
				//  1.1
				case 'integer':
					$return_value = intval($return_value);
					if (!is_integer($return_value)) {
						$return_value = $aDefault;
					} else {
						if ( true === is_array($range) )
						{
						    $this->__check_range($type, $return_value, $aDefault, $range);
						}
					}
					break;
				
				//  1.2 Only positive integers (without 0)
				case 'integer+':
					$return_value = intval($return_value);
					if (!is_integer($return_value))
					{
					    $return_value = $aDefault;
					}
					
					if ($return_value <= 0)
					{
					    $return_value = $aDefault;
					}
					
					if (true === is_array($range) )
					{
					    $this->__check_range($type, $return_value, $aDefault, $range);
					}
					break;
					
				//  1.3 Only negative integers (without 0)
				case 'integer-':
					$return_value = intval($return_value);
					if (!is_integer($return_value))
					{
					    $return_value = $aDefault;
					}
					
					if ( $return_value >= 0)
					{
					    $return_value = $aDefault;
					}
					
					if ( true === is_array($range) )
					{
					    $this->__check_range($type, $return_value, $aDefault, $range);
					}
					break;
            
                /*  1.4 decimal
                 *      Keep in mind that we only replace the "," (Comma) with "." (decimal-dot)!  
                 *      to save a valid value in the database.
                 *
                 *  e.g. "Win 1.234.300 Euro" will become "1234300"
                 *       "1.300.000,34" will become "1300000.34"
                 *       "1.300.000.27" will become "1300000.27"
                 *       "-23,99" will become "-23.99"
                 */ 
                case 'float':
                case 'double':
                case 'decimal': // -12.350,78 
                    //  1.4.1 remove all NONE numbers (but keep '.', ',' and '-')
                    $sPattern = "/[^0-9-,\.]/i";
                    $return_value = preg_replace (
                        $sPattern , 
                        "" ,
                        $return_value
                    );
                    
                    //  1.4.2 replace existing "," with "."
                    $return_value = str_replace(",", ".", $return_value);
                    
                    //  1.4.3 force to keep at last ONE dot (.)
                    $aTemp = explode(".", $return_value);
                    
                    //  1.4.3.1 more than one dot found!
                    if(count($aTemp) > 2)
                    {
                        $sPrefix = array_pop($aTemp);
                        $return_value = implode("", $aTemp).".".$sPrefix;    
                    }
                    
                    //  1.4.4 the range
                    if ( true === is_array($range) )
                    {
					    $this->__check_range($type, $return_value, $aDefault, $range);
                    }
					break;
                
                         
				//  [2] Strings
				//  2.1
				case 'string':
				case 'str':  // for backward compatibility only
					//	keep in mind that pdo add slashes automatically	via prepare and execute
					if (!is_string((string)$return_value))
					{
					    $return_value = $aDefault;
					}
					
					if ( true === is_array($range) )
					{
					    $this->__check_range($type, $return_value, $aDefault, $range);
					}
					break;
				
				//  2.2 string without any html tags
				case 'string_clean':
					
					if (!is_string((string)$return_value))
					{
					    $return_value = $aDefault;
					}
					else
					{
						$return_value = htmlspecialchars(strip_tags($return_value));
					}				
					break;	

                //  2.3 string with all html-tags converted to htmlspecialchars
				case 'string_chars':
					
					if (!is_string((string)$return_value))
					{
					    $return_value = $aDefault;
					}
					else
					{
					    $return_value = lib_lepton::getToolInstance("htmlpurifier")->purify($return_value);
						$return_value = htmlspecialchars($return_value);
					}				
					break;
                
                //  2.4 string without tags but allowed html-tags
				case 'string_allowed':
				
					if (!is_string((string)$return_value))
					{
					    $return_value = $aDefault;
					}
					else
					{
						if ( true === is_array($range) )
						{
							// range contains all allowed html-tags
							//  see: https://www.php.net/manual/de/function.strip-tags.php
							if (version_compare(PHP_VERSION, '7.4.0', '<'))
							{
							    $range = "<".implode("><", $range).">";
							}
							$return_value = strip_tags($return_value, $range);
						}
						else
						{
							$return_value = strip_tags($return_value, $range);
						}						
					}				
					break;						
				
				//  [3] EAN	
				case 'ean':
					if (false === $this->validate_ean13($return_value))
					{
					    $return_value = $aDefault;
					}
					break;
				
				//  [4] E-Mail
				case 'email':
					if ( false === LEPTON_handle::checkEmailChars($return_value ))
					{
						$return_value = '';
					}
					break;

                //  [5] Date(-s)
                //  [5.1]
				case 'date':
					$format = 'Y-m-d';
					$d = DateTime::createFromFormat($format, $return_value);
					$result = $d && $d->format($format) == $return_value;
					if ($result === false) {
						$return_value = $aDefault;
					}
					break;
                
                //  [5.2]
				case 'datetime':
					$format = 'Y-m-d H:i:s';
					$d = DateTime::createFromFormat($format, $return_value);
					$result = $d && $d->format($format) == $return_value;
					if ($result === false) {
						$return_value = $aDefault;
					}
					break;

                //  [6] Arrays
				case 'array':
					if(!is_array($return_value))
					{
					    $return_value = $aDefault;
					}
					break;
					
				//  [7]
				default:
				    // nothing above match.
				    break;
			}
		}
		return $return_value;
		
	}
	
	static public function add_slash( &$sText="" ) {
		
		if (substr($sText, 0,1) != "/")
		{
		    $sText = "/".$sText;
		}
		
		if (substr($sText, -1) != "/")
		{
		    $sText .= "/";
		}
	}
        
        private function __check_range($type, &$value, &$default, &$range)
        {

            if ($value === NULL)
            {
                return true;
            }

            if ( !array_key_exists('use', $range))
            {
                $range['use'] = 'default';
            }

            if ( !array_key_exists('min', $range))
            {
                $range['min'] = 0;
            }

            if ( !array_key_exists('max', $range))
            {
                $range['max'] = 255;
            }

            if ( !array_key_exists('char', $range))
            {
                $range['char'] = " ";
            }

            switch (strtolower ($type) )
            {
                case 'integer':
                case 'integer+':
                case 'integer-':
                case 'float':
                    if ( ($value < $range['min']) || ($value > $range['max']) )
                    {
                        switch (strtolower($range['use']))
                        {
                            case 'default' : $value = $default; break;
                            case 'min': $value = $range['min']; break;
                            case 'max': $value = $range['max']; break;
                            case 'near':
                                if ($value < $range['min'])
                                {
                                    $value = $range['min'];
                                }

                                if ($value > $range['max'])
                                {
                                    $value = $range['max'];
                                }
                                break;

                            default:
                                // nothing
                                break;
                        }
                    }
                    break;

                case 'string':
                    $nc = strlen($value);

                    if (($nc < $range['min']) || ($nc > $range['max']))
                    {

                        switch(strtolower($range['use']))
                        {
                            case 'default': $value = $default; break;
                            case 'fill':
                                for ($i=$nc; $i<=$range['min'];$i++)
                                {
                                    $value .= $range['char'];
                                }
                                break;

                            case 'cut':
                                $value = substr(
                                    $value,
                                    0,
                                    ( isset($range['max'])
                                        ? intval($range['max'])
                                        : $nc 
                                    )
                                );
                                break;

                            default:
                                // nothing - keep value as it is
                                break;
                        }
                    }
                    break;

                default:
                    // nothing
                    break;
            }
            return true;
        }

    /**
     *  since LEPTON 5.3
     *  
     */
    static public function getRequest( string $sField="" )
    {
        $sReturnValue = filter_input(
            INPUT_GET,
            $sField,
            FILTER_VALIDATE_REGEXP,
            [ "options"     => [
                "regexp"    => "~^[a-z0-9-_]{2,}$~i",
                "default"   => NULL
            ]]
        );
        
        if(NULL !== $sReturnValue)
        {
            return $sReturnValue;
        } else {
        
             return filter_input(
                INPUT_POST,
                $sField,
                FILTER_VALIDATE_REGEXP,
                [ "options"     => [
                    "regexp"    => "~^[a-z0-9-_]{2,}$~i",
                    "default"   => NULL
                ]]
            );
        }
    }
    
    /**
     *  LEPTON 5.3
     *  
     */
    static public function getPageID()
    {
        if(defined("PAGE_ID"))
        {
            return PAGE_ID;
        }
        
        $sField="page_id" ;
         
        $sReturnValue = filter_input(
            INPUT_GET,
            $sField,
            FILTER_VALIDATE_REGEXP,
            [ "options"     => [
                "regexp"    => "~^[1-9-]?[0-9]*$~",
                "default"   => NULL
            ]]
        );
        
        if(NULL !== $sReturnValue)
        {
            return $sReturnValue;
        } else {
        
             return filter_input(
                INPUT_POST,
                $sField,
                FILTER_VALIDATE_REGEXP,
                [ "options"     => [
                    "regexp"    => "~^[1-9-]?[0-9]*$~",
                    "default"   => NULL
                ]]
            );
        }
    }
}
