<?php

declare(strict_types=1);

/**
 * 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-2026 LEPTON Project
 * @link            https://lepton-cms.org
 * @license         https://gnu.org/licenses/gpl.html
 * @license_terms   please see LICENSE and COPYING files in your package
 *
 */


class LEPTON_core extends LEPTON_securecms
{
    use LEPTON_singleton;

    public static $instance;

    public string $string_secure = 'a-zA-Z0-9\-_\.';

    public string $password_chars = 'a-zA-Z0-9\_\-\!\$\ย\ง\#\*\+';

    public string $email_chars = 'a-zA-Z0-9@\_\-\.';

    public string $username_chars = 'a-zA-Z0-9@ \_\-,\.';
    
    public string $hex_chars = 'a-fA-F0-9\-';
    
    public static array $HEADERS = [
            'frontend' => [
                'css' => [],
                'js' => []
            ],
            'backend' => [
                'css' => [],
                'js' => []
            ]
        ];

    public static array $FOOTERS = [
            'frontend' => [
                'css' => [],
                'js' => []
            ],
            'backend' => [
                'css' => [],
                'js' => []
            ]
        ];
    
    public static bool $bUserGotAdminRights = false;
	
	public static array $aPageVisibility = ['public', 'private', 'registered', 'hidden', 'none'];
    
    /**
     *  Constructor of the class
     */
    public function __construct()
    {

    }
    
    /**
     *  Check whether a page is visible or not.
     *  This will check page-visibility and user- and group-rights.
     *
     *  @return bool false: if page-visibility is 'none' or 'deleted', or page-vis. is 'registered' or 'private' and user isn't allowed to see the page.
     *               true: if page-visibility is 'public' or 'hidden', or page-vis. is 'registered' or 'private' and user _is_ allowed to see the page.
     *
     */
    public function page_is_visible( $page ): bool
    {
        // First check if visibility is 'none', 'deleted'

        $show_it = false;
        switch ($page['visibility'])
        {
            case 'hidden':
            case 'public':
                $show_it = true;
                break;

            case 'private':
            case 'registered':
                if ($this->is_authenticated() === true)
                {
                    $show_it = ($this->is_group_match($this->getValue('groups_id', 'string', 'session',','), $page['viewing_groups']) );
                }
                break;

            case 'none':
            case 'deleted':
            default:
                $show_it = false;
                break;                
        }

        return ($show_it);
    }
    
    /**
     *  Check if a given section is active.
     *
     *  @param  int $section_id A valid section_id.
     *  @return boolean             True if the section is active, otherwise false.
     *
     */
    public function section_is_active(int $section_id): bool
    {
        $now = time();
        $sql = "
            SELECT COUNT(*) 
            FROM    `" . TABLE_PREFIX . "sections` 
            WHERE   (" . $now . " BETWEEN `publ_start` AND `publ_end`) 
                OR  (" . $now . " > `publ_start` AND `publ_end`=0) 
                AND `section_id`=" . $section_id;

        return (LEPTON_database::getInstance()->get_one($sql) > 0);
    }

    /**
     *  Check if there is at least one active section on this page.
     *
     *  @param  array   $page   An assoc. array with the page-values.
     *  @return boolean         True there is at least one active section on this page, otherwise false.
     *
     */
    public function page_is_active(array $page ): bool
    {
        $now = time();
        $sql = "
            SELECT COUNT(*) 
            FROM `".TABLE_PREFIX."sections` 
            WHERE `page_id` = ".$page['page_id']." 
            AND (".$now." BETWEEN `publ_start` AND `publ_end` OR (".$now." > `publ_start` AND `publ_end`= 0))
        ";
             
        return (LEPTON_database::getInstance()->get_one($sql) > 0 );
    }

    /**
     *  Check whether we should show a page or not (for front-end).
     *
     *  @param  array|null   $page   An assoc. array with the page-values.
     *  @return boolean         True there is at least one active section on this page, otherwise false.
     */
    public function show_page(array|null $page ): bool
    {
        if (!is_array($page))
        {
            $page = [];
            LEPTON_database::getInstance()->execute_query(
                "SELECT `page_id`, `visibility` FROM `" . TABLE_PREFIX . "pages` WHERE `page_id`=" . (int) $page,
                true, 
                $page,
                false
            );
            
            if (empty($page))
            {
                return false;
            }
        }
        return ($this->page_is_visible($page) && $this->page_is_active($page));
    }

    // Check if the user is already authenticated or not
    public function is_authenticated(): bool
    {
        return (    (isset( $_SESSION[ 'USER_ID' ] ) )      // gesetzt
                &&  ($_SESSION[ 'USER_ID' ] != "")          // nicht leer
                &&  (is_numeric( $_SESSION[ 'USER_ID' ] ) ) // eine Zahl
        );
    }

    /**
     * Returns the full URL to a given page(-link).
     * Takes care for 'mailto:' links and even qualified links.
     *
     * @param string $link
     * @return string
     */
    public function buildPageLink(string $link): string
    {
        // Check for :// in the link (used in URL's) as well as mailto:
        if (strstr($link, '://') == '' && !str_starts_with($link, 'mailto:'))
        {
            return LEPTON_URL . PAGES_DIRECTORY . $link . PAGE_EXTENSION;
        }
        else
        {
            return $link;
        }
    }

    
    /**
     * Get (POST|SESSION|GET) data check through LEPTON_request
     * More flexible way to take also care fore $_SESSION, or e.g. $_SERVER
     * 
     * @param string $lookForName   The name of the key(field_name) we are looking for, e.g. 'city'.
     * @param string $type          Optional the type for, e.g. 'integer'. Default is 'string_clean'.
     * @param string $requestFrom   Optional where to look for (post,session,get,request). Default is 'post'.
     * @param string $dividerString Optional divider (e.g. ",") "X" (capital x) means "ignore".
     * @param string|int|array|null $default  Optional default value. Standard is NULL. Tip: use typed-params call: e.g. default:'-whatever' 
     * 
     * @code get the value from a 'email'[-field] in $_POST: LEPTON_core::getValue('email', 'email', post);
     * @code get the value from a 'user_id' in $_SESSION: LEPTON_core::getValue('user_id', 'integer', 'session');
     * @code get the values from a comma seperated string in $_SESSION: LEPTON_core::getValue('groups_id', 'string_clean', 'session', ',');
     * @code get the value from $_Post['city'] simply use: LEPTON_core::getValue('city');
     *
     * @code get the value from $_Post['city'] with default: LEPTON_core::getValue('city', default:'33689');
     *
     * @code get the value from $_Post['title'] with range: LEPTON_core::getValue('title', 'string_allowed', range: ['b','i','em']);
     *  -- will strip all given tags exept 'b','i' and 'em' in the value (use stripTags)
     * 
     * @code get the value from $_Post['jcom'] with range: LEPTON_core::getValue('jcom', 'integer', range: ['min' => 60, 'max' => 90, 'use' => 'near']);
     *  -- will force the value to be between 60 and 90
     * 
     * @return string|int|array|null The passed result as string, integer, array or null.
     *
     */
    public static function getValue(
        string $lookForName,
        string $type = "string_clean",
        string $requestFrom = "post",
        string $dividerString = "X",
        string|int|array|null $default = null,
        string|int|array|null $range = null
    ): string|int|array|null
    {
        // $_SESSION keys are always upper case
        if ($requestFrom == 'session')
        {
            $lookForName = strtoupper($lookForName);
        }

        LEPTON_request::getInstance()->setStrictLookInside($requestFrom);
        $aField = [ $lookForName    => [
            'type'    => $type,
            'default' => $default,
            'range'   => $range
            ]
        ];
        $sTested = LEPTON_request::getInstance()->testValues($aField);

        // L*VII.3 if result is NULL or EMPTY and the default value is set, use the default
        if ((is_null($sTested[$lookForName]) || empty($sTested[$lookForName])) && (!empty($default)))
        {
            $sTested[$lookForName] = $default;
        }
        // make sure that comma seperated strings are exploded
        if ($dividerString != 'X')
        {
            if (!is_array($sTested[$lookForName]))
            {
                $sTested[$lookForName] = explode($dividerString, $sTested[$lookForName] ?? "");
            } else {
                foreach ($sTested[$lookForName] as &$item)
                {
                    $item = explode($dividerString, $item ?? "");
                }
            }
        }
        return $sTested[$lookForName];
    }

    /**
     * Check if one or more group_ids are in both group_lists
     *
     * @access public
     * @param mixed $groups_list1   An array or a comma seperated list of group-ids
     * @param mixed $groups_list2   An array or a comma seperated list of group-ids
     * @return bool: true there is a match, otherwise false
     */
    public function is_group_match(int|string|array $groups_list1 = '', int|string|array $groups_list2 = ''): bool
    {
        if ($groups_list1 == '')
        {
            return false;
        }
        if ($groups_list2 == '')
        {
            return false;
        }
        if (!is_array($groups_list1))
        {
            $groups_list1 = explode( ',', $groups_list1 );
        }
        if (!is_array($groups_list2))
        {
            $groups_list2 = explode( ',', $groups_list2 );
        }

        return (sizeof(array_intersect($groups_list1, $groups_list2)) != 0);
    }

    /**
     * Internal function, only used for get_page_headers and get_page_footers
     *
     * @param string $for
     * @param string $path
     * @param bool $footer
     * @return void
     */
    public static function addItems(string $for = 'frontend', string $path = LEPTON_PATH, bool $footer = false): void
    {
        $trail = explode('/', $path);
        $subdir = array_pop($trail);
    
        $mod_headers = [];
        $mod_footers = [];

        if ($footer)
        {
            $add_to = &self::$FOOTERS;
            $to_load = 'footers.inc.php';
        }
        else
        {
            $add_to = &self::$HEADERS;
            $to_load = 'headers.inc.php';
        }

        require $path.'/'.$to_load;

        if (true === $footer)
        { 
            $aRefArray = &$mod_footers;
        }
        else
        {
            $aRefArray = &$mod_headers;
        }

        if (count($aRefArray))
        {
            foreach (['css', 'js'] as $key)
            {
                if (!isset($aRefArray[$for][$key]))
                {
                    continue;
                }
                foreach ($aRefArray[$for][$key] as &$item)
                {
                    // let's see if the path is relative (i.e., does not contain the current subdir)
                    if ((isset($item['file']))
                        && (!preg_match("#/$subdir/#", $item['file']))
                        && (file_exists($path.'/'.$item['file']))
                    )
                    {
                        // treat path as relative, add modules subfolder
                        $item['file'] = str_ireplace(LEPTON_PATH, '', $path).'/'.$item['file'];
                    }
                    
                    $is_ok = true;
                    if ($key === "css") {
                        foreach ($add_to[$for][$key] as $temp_ref)
                        {
                            if ($temp_ref['file'] == $item['file'])
                            {
                                $is_ok = false;
                            }
                        }
                    } elseif ($key === "js")
                    {
                        foreach ($add_to[$for][$key] as $temp_ref)
                        {
                            if ($item === $temp_ref)
                            {
                                $is_ok = false;
                            }
                        }
                    
                    }

                    if (true === $is_ok)
                    {
                        $add_to[$for][$key][] = $item;
                    }
                }
            }
        }

        if ($footer && file_exists($path.$for.'_body.js'))
        {
            $add_to[$for]['js'][] = '/modules/'.$subdir.'_body.js';
        }
        
    }

    /**
     * @param string $html The hole HTML page. Call by reference.
     * @param object $oReference reference of a given object instance. Call by reference.
     * @internal call for protected functions, this time only one but could be more methods!
     */
    public function getProtectedFunctions(string &$html, object &$oReference): void
    {
        $this->addLepToken($html, $oReference);
    }

    /**
     *    @param    string  $html    The hole HTML page. Call by reference.
     *    @param    object  $oReference      Ref. of a given object instance. Call by reference.
     */
    protected function addLepToken(string &$html, object &$oReference): void
    {
        /**
         * @notice erpe: 2023-01-17 - seems that this is handled in LEPTON_securecms (public function checkLepToken()) and LEPTON_admin (line 189)
         */
        if (!LEPTOKEN_LIFETIME)
        {
            return;
        }

        $token = $oReference->createLepToken();

        $token1      = "$1?leptoken=".$token."$3"; // no parameters so far
        $token2      = "leptoken=".$token; // for replacing placeholder in JS functions
        $token3      = "$1&leptoken=".$token."$3"; // with existing parameters, produces html-valid code
        $token4      = "$1?leptoken=".$token."$2"; // for special cases
        $hiddentoken = "$1\n<span><input type='hidden' name='leptoken' value='".$token."' /></span>\n"; // for GET forms, add a hidden field too

        // finds absolute Links with Parameter:
        $qs   = '~((href|action|window\.location)\s?=\s?[\'"]' . LEPTON_URL . '[\w\-\./]+\.php\?[\w\-\.=&%;/]+)([#[\w]*]?[\'"])~';
        $html = preg_replace( $qs, $token3, $html, -1 );

        // finds absolute Links without Parameter:
        $qs   = '~((href|action|ajaxfilemanagerurl|window\.location)\s?=\s?[\'"]' . LEPTON_URL . '[\w\-\./]+\.php)([#[\w]*]?[\'"])~';
        $html = preg_replace( $qs, $token1, $html, -1 );

        // finds relative Links with Parameter:
        $qs   = '~((href|action|window\.location)\s?=\s?[\'"][\w/]+\.php\?[\w\-\.=%&;/]+)([#[\w]*]?[\'"])~';
        $html = preg_replace( $qs, $token3, $html, -1 );

        // finds relative Links without Parameter:
        $qs   = '~((href|action|window\.location)\s?=\s?[\'"][\w/]+\.php)([#[\w]*]?[\'"])~';
        $html = preg_replace( $qs, $token1, $html, -1 );

        // finds Start page without Parameter:
        $qs   = '~(href\s?=\s?[\'"]' . LEPTON_URL . ')([\'"])~';
        $html = preg_replace( $qs, $token4, $html, -1 );

        // finds Testmail in Options:
        $qs   = '~(send_testmail\(\'' . ADMIN_URL . '/settings/ajax_testmail\.php)(\'\))~';
        $html = preg_replace( $qs, $token4, $html, -1 );

        // finds forms with method=get and adds a hidden field
        $qs   = '~(<form\s+action=[\'"][\w:\.\?/]+leptoken=\w{32}[\'"]\s+method=[\'"]get[\'"]\s*>)~';
        $html = preg_replace( $qs, $hiddentoken, $html, -1 );

        // set leptoken in JS functions
        $qs   = '~leptokh=#-!leptoken-!#~';
        $html = preg_replace( $qs, $token2, $html, -1 );	
    }


    /**
     * Create directories recursive
     *
     * @param string $dir_name - directory to create
     * @param string|null $dir_mode - access mode
     * @return boolean result of operation
     */ 
    static public function make_dir(string $dir_name, string|null $dir_mode = NULL): bool
    {
        if ($dir_mode == NULL)
        {
            $dir_mode = (int) octdec( STRING_DIR_MODE );
        }
        
        if (!is_dir($dir_name))
        {
            $umask = umask(); // get current mask
            $result = mkdir($dir_name, $dir_mode, true);
			if($result == false) 
			{
				echo(LEPTON_tools::display('Cannot create directory!', 'pre','ui red message'));
				return false;
			}
            umask($umask);
            return true;
        }
		else
		{
			return true;
		}
    }

    /**
     * not used on Windows Systems, values from table settings
     *
     * @access public
     * @param  string   $name - directory or file
     * @return bool
     *
     **/
    static public function change_mode(string $name ): bool
    {
        if (OPERATING_SYSTEM != 'windows')
        {
            $mode = (is_dir($name)) ? (int)octdec(STRING_DIR_MODE) : (int)octdec(STRING_FILE_MODE);
            if (file_exists($name))
            {
                $umask = umask();       // get current
                chmod($name, $mode);
                umask($umask);
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return true;
        }
    }


    /**
     * Creates a new access file for a given page_id.
     *
     * @param string    $filename  Full path
     * @param int       $page_id      Internal page_id
     * @return bool     True if success.
     */
    static public function create_access_file(string $filename, int $page_id): bool
    {
        global $admin, $MESSAGE;
        $pages_path = LEPTON_PATH . PAGES_DIRECTORY;
        $rel_pages_dir = str_replace($pages_path, '', dirname($filename));
        $rel_filename = str_replace($pages_path, '', $filename);
        // root_check prevent system directories and important files from being overwritten if PAGES_DIR = '/'
        $denied = false;
        if (PAGES_DIRECTORY == '')
        {
            $forbidden = [
                'account',
                'admins',
                'framework',
                'include',
                'install',
                'languages',
                'media',
                'modules',
                'page',
                'search',
                'temp',
                'templates',
                'index.php',
                '/config/config.php'
            ];
            $search = explode('/', $rel_filename);
            // we need only the first level
            $denied = in_array($search[1], $forbidden);
        }
        if ((true === is_writable($pages_path)) && (false === $denied))
        {
            // First make sure parent folder exists
            $parent_folders = explode('/', $rel_pages_dir);
            $parents = '';
            foreach ($parent_folders as $parent_folder)
            {
                if ($parent_folder != '/' && $parent_folder != '')
                {
                    $parents .= '/' . $parent_folder;
                    if (!file_exists($pages_path . $parents))
                    {
                        LEPTON_core::make_dir( $pages_path . $parents );
                        LEPTON_core::change_mode( $pages_path . $parents );
                    }
                }
            }
            $step_back = str_repeat('../', substr_count($rel_pages_dir, '/') + (PAGES_DIRECTORY == "" ? 0 : 1));
            $content = '<?php'."\n";
            $content .= "/**\n *  This file is autogenerated by LEPTON - Version: ".LEPTON_VERSION."\n";
            $content .= " *  Do not modify this file!\n */\n";
            $content .= "    ".'$page_id = '.$page_id.';'."\n";
            $content .= "    ".'require_once \''.$step_back.'index.php\';'."\n";

            /**
             *  write the file
             *
             */
            $fp = fopen($filename, 'w');
            if ($fp)
            {
                fwrite($fp, $content, strlen($content));
                fclose($fp);
                /**
                 *  Chmod the file
                 *
                 */
                LEPTON_core::change_mode($filename);
                /**
                 *    Looking for the index.php inside the current directory.
                 *    If not found - we just copy the master_index.php from the admin/pages
                 *
                 */
                $temp_index_path = dirname($filename)."/index.php";
                if (!file_exists($temp_index_path))
                {
                    $origin = ADMIN_PATH."/pages/master_index.php";
                    if (file_exists($origin))
                    {
                        copy($origin, $temp_index_path);
                    }
                }
            }
            else
            {
                $admin->print_error($MESSAGE['PAGES_CANNOT_CREATE_ACCESS_FILE']."<br />Problems while trying to open the file!");
                return false;
            }
            return true;
        }
        else
        {
            $admin->print_error($MESSAGE['PAGES_CANNOT_CREATE_ACCESS_FILE']);
            return false;
        }
    }

    /**
     * Counts the levels from given page_id to root
     *
     * @access public
     * @param  int  $iPageId
     * @return int  The current level of the given page_id >== 0
     *
     **/
    static public function level_count(int $iPageId ): int
    {
        $database = LEPTON_database::getInstance();
        // Get page parent
        $iParent = $database->get_one('SELECT `parent` FROM `'.TABLE_PREFIX.'pages` WHERE `page_id` = '.$iPageId);
        if ($iParent > 0)
        {
            // Get the level of the parent
            $iLevel = $database->get_one('SELECT `level` FROM `'.TABLE_PREFIX.'pages` WHERE `page_id` = '.$iParent);
            return $iLevel + 1;
        }
        else
        {
            return 0;
        }
    }


    /**
     *  Function to get all sub-pages id's from a given page id.
     *
     *  @param  int     $parent A valid page_id as 'root'.
     *  @param  array   $subs   A given linear array to store the results. Call-by-reference!
     *
     *  @return	void    Keep in mind, that param "subs" is passed-by-reference!
     *
     *  @example        $all_subpages_ids = [];
     *                  LEPTON_core::get_subs( 5, $all_subpages_ids);
     *
     *                  Will result in a linear list like e.g. "[5,6,8,9,11,7]" as the sub-ids
     *                  are sorted by position (in the page-tree);
     *
     */
    static public function get_subs(int $parent, array &$subs): void
    {
        $database = LEPTON_database::getInstance();

        // Get id's
        $all = [];
        $database->execute_query(
            "SELECT `page_id` FROM `".TABLE_PREFIX."pages` WHERE `parent` = ".$parent." ORDER BY position",
            true,
            $all,
            true
        );

        foreach ($all as &$aTempPageRef)
        {
            $subs[] = $aTempPageRef['page_id'];

            // Get subs of this sub - recursive call!
            self::get_subs($aTempPageRef['page_id'], $subs);
        }
    }


    /**
     * Delete a page
     *
     * @access public
     * @param  integer $page_id
     * @return void
     *
     **/
    static public function delete_page(int $page_id): void
    {
        $database = LEPTON_database::getInstance();
        $admin = self::getGlobal('admin');
        $MESSAGE = self::getGlobal('MESSAGE');
        $section_id = self::getGlobal('section_id');

        LEPTON_handle::register("rm_full_dir");
        
        // Find out more about the page
        $page_info = [];
        $database->execute_query(
            'SELECT link, parent FROM '.TABLE_PREFIX.'pages WHERE page_id = '.$page_id,
            true,
            $page_info,
            false
        );

        if (empty($page_info))
        {
            $admin->print_error($MESSAGE['PAGES_NOT_FOUND']);
        }

        // Get the sections that belong to the page
        $all_sections = [];
        $database->execute_query(
            'SELECT section_id, module FROM '.TABLE_PREFIX.'sections WHERE page_id = '.$page_id,
            true,
            $all_sections
        );

        foreach($all_sections as &$section)
        {
            // Set section id
            $section_id = $section['section_id'];

            // Include the modules delete file if it exists
            if (file_exists(LEPTON_PATH.'/modules/'.$section['module'].'/delete.php'))
            {
                include LEPTON_PATH.'/modules/'.$section['module'].'/delete.php';
            }
        }
        
        // Update the pages table
        $database->simple_query("DELETE FROM ".TABLE_PREFIX."pages WHERE page_id = ".$page_id);
        
        // Update the sections table
        $database->simple_query("DELETE FROM ".TABLE_PREFIX."sections WHERE page_id = ".$page_id);

        // Include the ordering class or clean-up ordering
        $order = LEPTON_order::getInstance(TABLE_PREFIX.'pages', 'position', 'page_id', 'parent');
        $order->clean($page_info['parent']);

        // Unlink the page access file and directory
        $directory = LEPTON_PATH . PAGES_DIRECTORY . $page_info['link'];
        $filename  = $directory . PAGE_EXTENSION;
        $directory .= '/';
        if (file_exists($filename))
        {
            if (!is_writable(LEPTON_PATH . PAGES_DIRECTORY . '/'))
            {
                $admin->print_error($MESSAGE['PAGES_CANNOT_DELETE_ACCESS_FILE']);
            }
            else
            {
                unlink($filename);
                if (file_exists($directory) && (rtrim($directory, '/') != LEPTON_PATH . PAGES_DIRECTORY) && ($page_info['link'][0] != '.'))
                {
                    rm_full_dir($directory);
                }
            }
        }
    }

    /**
     * @param string $name
     * @return int|string|array|object|null
     */
    static public function getGlobal(string $name): null|int|string|array|object
    {
        $returnValue = null;
        if (isset($GLOBALS[$name]))
        {
            $returnValue = &$GLOBALS[$name];
        }
        return $returnValue;
    }

    /**
     * Basic functions are used for frontend.
     *
     * @return void
     */
    static function registerBasicFunctions(): void
    {
        $functionListToRegister = [
            "get_page_headers",
            "get_page_footers",
            "page_content",
            "easymultilang_menu"
        ];

        LEPTON_handle::register($functionListToRegister);
    }

    /**
     * From this time on we can also use code snippets in the backend.
     * @return void
     */
    static function loadCodeSnippets(): void
    {
        $snippets = [];
        LEPTON_database::getInstance()->execute_query(
            "SELECT `directory` FROM `".TABLE_PREFIX."addons` WHERE `function` = 'snippet'",
            true,
            $snippets,
            true
        );

        foreach ($snippets as $snippet)
        {
            $tempPath = LEPTON_PATH."/modules/".$snippet['directory']."/include.php";
            if (file_exists($tempPath))
            {
                require $tempPath;
            }
        }
    }

    /**
     * Test if there is an entry in the database matching to a given value. Could be an ip or an e-mail.
     * 
     * @param string $value
     * @return bool     True if there is a matching entry in the database.
     */
    static public function check_entry(string $value): bool
    {
        // get all keepouts
        $all_entries = [];
        LEPTON_database::getInstance()->execute_query(
            "SELECT * FROM ".TABLE_PREFIX."keepout ORDER BY id DESC",
            true,
            $all_entries,
            true
        );

        if (!empty($all_entries))
        {
            foreach ($all_entries as $check)
            {
                if (str_contains($check['email'], '*'))
                {
                    $check_email = str_replace('*','',$check['email']);
                    if (str_contains($value, $check_email))
                    {
                        return true;
                    }
                }

                if ($value == $check['ip'] || $value == $check['email'])
                {
                    return true;
                }
            }
            return false;
        }
        return false;
    }
    
    /**
     * Has the current user admin-rights?
     * 
     * @return bool
     */
    static public function userHasAdminRights(): bool
    {
        if (self::$bUserGotAdminRights == false)
        {
            //  Current user has admin rights?
            $aUser = explode(",", ($_SESSION['GROUPS_ID'] ?? ""));
            self::$bUserGotAdminRights = (in_array(1, $aUser));
        }

        return self::$bUserGotAdminRights;
    }

    /**
     * Intersection of Settings/Whitelist and lib_r_filemanager const
     * 
     * @return array
     */
    static public function imageTypesAllowed(): array
    {
        $aWhiteList = explode(',', UPLOAD_WHITELIST);
        $aImageTypes = lib_r_filemanager::allowed_image_types;

        return array_merge(array_intersect($aWhiteList, $aImageTypes), []);
    }

}
